This is an R Markdown Notebook for analysis using data on the DC Bus System (WMATA Metrobus). The data were obtained here:

https://planitmetro.com/2016/11/16/data-download-metrobus-vehicle-location-data/

Load the packages to be used.

package ‘proto’ was built under R version 3.2.5package ‘RSQLite’ was built under R version 3.2.5package ‘tidyr’ was built under R version 3.2.5package ‘plyr’ was built under R version 3.2.5package ‘stringr’ was built under R version 3.2.5package ‘geosphere’ was built under R version 3.2.5package ‘ggplot2’ was built under R version 3.2.5package ‘rgdal’ was built under R version 3.2.5package ‘maptools’ was built under R version 3.2.5package ‘rgeos’ was built under R version 3.2.5package ‘lattice’ was built under R version 3.2.5package ‘cluster’ was built under R version 3.2.5package ‘dbscan’ was built under R version 3.2.5package ‘factoextra’ was built under R version 3.2.5

Get the Bus data.

First let’s check the working directory.


getwd()

Then, actually get the data.

Put the daily data together.


AllDays <- bind_rows(list(Oct03Raw, Oct04Raw, Oct05Raw, Oct06Raw, Oct07Raw),
                     .id = c("group")
                    )
# dim(AllDays)
str(AllDays)

Deleting old data frames.


for (i in 3:7){
  rm(list = ls(pattern = paste0("Oct0", i, "Raw")
              )
    )
  
  message("Deleting Oct0", i, "Raw")
  }

Updating variable types.

Then, sorting the data and adding a RowNumber (to be used for identifying rows later in the analyses.)


rm(i)


AllDays$group <- factor(AllDays$group)
AllDays$Route_Direction <- factor(AllDays$Route_Direction)
AllDays$Event_Time <- as.POSIXct(AllDays$Event_Time, format = "%m-%d-%y %I:%M:%S %p")
AllDays$Departure_Time <- as.POSIXct(AllDays$Departure_Time, format = "%m-%d-%y %I:%M:%S %p")

str(AllDays)


AllDays_Sorted <- arrange(AllDays,
                          Bus_ID,
                          Event_Time
                         ) %>% 
  mutate(RowNum_OG = row_number() # this is useful in identify the row later on
        )

rm(AllDays)
str(AllDays_Sorted)

# View(head(AllDays_Sorted, 100))

Inspecting the values of Stop_ID, and finding that it can take the values “” (blank) and “NULL”.


View(group_by(AllDays_Sorted,
              Stop_ID
             ) %>% 
       summarise(
         Cnt = n()
         ) %>% 
       arrange(Stop_ID)
    )

View(filter(AllDays_Sorted,
            is.na(Stop_ID) |
              Stop_ID == "" |
              Stop_ID == "NULL"
           ) %>% 
       arrange(Stop_Desc)
    )

Creating a table of distinct Stop_Desc values when Stop_ID is “” (blank) or “NULL”.


StopID_New <- filter(AllDays_Sorted,
                     is.na(Stop_ID) |
                       Stop_ID == "" |
                       Stop_ID == "NULL"
                    ) %>% 
  select(Stop_ID, Stop_Desc) %>% 
  distinct() %>% 
  arrange(Stop_ID, Stop_Desc) %>% 
  mutate(StopID_New = 1:nrow(.)
        )

View(StopID_New)
StopID_New

Creating a full updated table by filling in StopID_New for when Stop_ID is “” (blank) or NULL.


AllDays_StopIDNew <- left_join(AllDays_Sorted,
                               select(StopID_New,
                                      Stop_Desc,
                                      StopID_New
                                     ),
                               by = c("Stop_Desc" = "Stop_Desc")
                              ) %>% 
  mutate(StopID_Clean = ifelse(is.na(StopID_New),
                               Stop_ID,
                               StopID_New
                              ),
         StopID_Indicator = factor(ifelse(is.na(StopID_New),
                                          "ID_OK",
                                          "ID_Bad"
                                         )
                                  )
        )

rm(StopID_New)
rm(AllDays_Sorted)
str(AllDays_StopIDNew)

# View(tail(AllDays_StopIDNew, 500))
# View(filter(AllDays_StopIDNew,
#             Stop_Desc == "METROWAY ANNNOUCEMNT CORR"
#            )
#     )

Lat Long stats for pulling in Zip codes later.


LL_Stats <- group_by(AllDays_StopIDNew,
                     StopID_Clean
                    ) %>% 
  summarise(Lat_Mean = mean(Latitude, na.rm = TRUE),
            Lat_Med = median(Latitude, na.rm = TRUE),
            Lng_Mean = mean(Longitude, na.rm = TRUE),
            Lng_Med = median(Longitude, na.rm = TRUE)
           ) %>% 
  mutate(Lat_MeaLessMed = Lat_Mean - Lat_Med,
         Lng_MeaLessMed = Lng_Mean - Lng_Med,
         RowNum = row_number()
        )

str(LL_Stats)
summary(LL_Stats)

View(head(arrange(LL_Stats,
                  Lat_MeaLessMed
                 ),
          500
         )
    )

View(head(arrange(LL_Stats,
                  desc(Lat_MeaLessMed)
                 ),
          500
         )
    )

View(head(arrange(LL_Stats,
                  Lng_MeaLessMed
                 ),
          500
         )
    )

View(head(arrange(LL_Stats,
                  desc(Lng_MeaLessMed)
                 ),
          500
         )
    )

Pulling in Zip Code data from api.geonames.org.


# URL EXAMPLE:
# http://api.geonames.org/findNearbyPostalCodesJSON?lat=38.89560&lng=-76.94873&radius=0&username=supermdat

url_1 <- "http://api.geonames.org/findNearbyPostalCodesJSON?lat="
url_2 <- "&lng="
url_3 <- "&radius=0&username="
username <- "supermdat"


# need to group in bunches as http://api.geonames.org limits pulls to 2000 per hour


##### Store everything in multiple lists
pages1 <- list()


system.time(

for(i in 1:1000){
  lat <- filter(LL_Stats,
                RowNum == i
               ) %>%
    select(Lat_Med)
  
  lng <- filter(LL_Stats,
                RowNum == i
               ) %>%
    select(Lng_Med)
  
  APIData1 <- fromJSON(paste0(url_1,
                              lat,
                              url_2,
                              lng,
                              url_3,
                              username
                             ),
                       flatten = TRUE
                      )
  
  message("Retrieving Zip Code ", i)
  
  pages1[[i]] <- APIData1$postalCodes
  
}
)


##### Combine the lists into one page
Zips1 <- rbind.pages(pages1[sapply(pages1, length) > 0])


##### Combine all pages
Zips_All <- bind_rows(Zips0,
                      Zips1,
                      Zips2,
                      Zips3,
                      Zips4,
                      Zips5,
                      Zips6,
                      Zips7,
                      Zips8,
                      Zips9,
                      Zips10,
                      .id = "id"
                     ) %>% 
  mutate(UniqueLatLng = paste(lat, lng, sep = "__")
        )

# str(Zips_All)
# View(head(Zips_All))


# str(LL_Stats)
LL_Stats_UnqLatLng <- mutate(LL_Stats,
                             UniqueLatLng = paste(Lat_Med, Lng_Med, sep = "__")
                            )

# str(LL_Stats_UnqLatLng)
# View(head(LL_Stats_UnqLatLng))


LL_StatsZips <- left_join(LL_Stats_UnqLatLng,
                          Zips_All,
                          by = c("UniqueLatLng" = "UniqueLatLng")
                         )

str(LL_StatsZips)
# View(head(LL_StatsZips))

# Not sure whey these couldn't be found (why they're NA)
View(filter(LL_StatsZips,
            is.na(postalCode)
           )
    )

Join to create one dataset that also includes Zip variables.


rm(url_1, url_2, url_3, username, pages0, pages1, pages2, pages3, pages4, pages5, pages6, pages7, pages8, pages9, pages10, i, lat, lng, APIData0, APIData1, APIData2, APIData3, APIData4, APIData5, APIData6, APIData7, APIData8, APIData9, APIData10, LL_Stats, LL_Stats_UnqLatLng)


AllDays_Zips <- left_join(AllDays_StopIDNew,
                          LL_StatsZips,
                          by = c("StopID_Clean" = "StopID_Clean")
                         ) %>% 
  rename(Stop_State = adminCode1,
         Stop_County = adminName2,
         Stop_City = placeName,
         Stop_Zip = postalCode
         )

rm(AllDays_StopIDNew, LL_StatsZips)
str(AllDays_Zips)

Updating variable types.


AllDays_Zips$Stop_State <- factor(AllDays_Zips$Stop_State)
AllDays_Zips$Stop_County <- factor(AllDays_Zips$Stop_County)
AllDays_Zips$Stop_Zip <- factor(AllDays_Zips$Stop_Zip)
AllDays_Zips$Stop_City <- factor(AllDays_Zips$Stop_City)

AllDays_Zips$distance <- as.numeric(AllDays_Zips$distance)
AllDays_Zips$countryCode <- factor(AllDays_Zips$countryCode)
AllDays_Zips$adminName1 <- factor(AllDays_Zips$adminName1)

str(AllDays_Zips)

Feature engineering.

Inspecting incidences of consecutive Stop_IDs. This is done because investigation showed that many conseutive events occurr at the same Stop_ID, but with various Dwell_Times, Odometer_Distances, etc. All of which affect calculations and analyses.

Create data on the runs (consecutive Stop_IDs).


StopID_Runs <- rle(AllDays_Zips$StopID_Clean)

StopID_Runs$ends <- cumsum(StopID_Runs$lengths)

StopID_Runs$starts <- ifelse(is.na(lag(StopID_Runs$ends)
                                  ),
                             1,
                             lag(StopID_Runs$ends) + 1
                            )

str(StopID_Runs)
# class(StopID_Runs)
# 
# StopID_Runs_df <- data.frame(unclass(StopID_Runs))
# str(StopID_Runs_df)
# class(StopID_Runs_df)
# rm(StopID_Runs_df)

Trying to link data on RunsGroups with the original data (AllDays_Sorted). The goal is to select only one record per RunsGroup - that being the record with the longest Dwell_Time.

I attempted this computation using both data.frames (dplyr) and data.tables (data.table). However, with 2,809,062 rows in one dataset and 3,119,443 rows in the other dataset, the current computation time is over 5 days…so I’m trying a different strategy to only select the first record in a run.


# Create a RunsGroup variable for each run
# StopID_Runs_df$RunsGroup <- paste0("g", seq(1:nrow(StopID_Runs_df)
#                                            )
#                                   )
# 
# str(StopID_Runs_df)
# head(StopID_Runs_df, 25)
# tail(StopID_Runs_df, 25)
# 
# StopID_Runs_df <- StopID_Runs_df %>% 
#   mutate(RowNum = row_number()
#         )
# 
# str(StopID_Runs_df)
# head(StopID_Runs_df, 25)
# tail(StopID_Runs_df, 25)
# 
# 
# # Converting to data.tables for, hopefully, improved performance (speed) in computation
# StopID_Runs_dt <- data.table(StopID_Runs_df)
# setkey(StopID_Runs_dt, RowNum)
# str(StopID_Runs_dt)
# 
# AllDays_Sorted_dt <- data.table(AllDays_Sorted)
# setkey(AllDays_Sorted_dt, RowNum_OG)
# str(AllDays_Sorted_dt)
# # rm(AllDays_Sorted_dt)
# 
# 
# # Actual loop to perform the computations and link to original data (AllDays_Sorted_dt)
# GroupData <- list()
# for(i in 1:nrow(StopID_Runs_dt)
#    ) {
#   assign(paste0("group_", i),
#            StopID_Runs_dt[RowNum == i, RunsGroup]
#           )
# 
#     #####  The code below is the same code as above, but done with dplyr  #####
# 
#     # assign(paste0("group_", i),
#   #        filter(StopID_Runs_df,
#   #               RowNum == i
#   #              ) %>% 
#   #          select(RunsGroup)
#   #       )
# 
#   assign(paste0("group_", i, "_start"),
#          StopID_Runs_dt[RowNum == i, starts]
#         )
# 
#   assign(paste0("group_", i, "_end"),
#          StopID_Runs_dt[RowNum == i, ends]
#         )
# 
#   assign(paste0("group_", i, "_rows"),
#          AllDays_Sorted_dt[RowNum_OG >= as.numeric(get(paste0("group_", i, "_start")
#                                                       )
#                                                   ) &
#                            RowNum_OG <= as.numeric(get(paste0("group_", i, "_end")
#                                                       )
#                                                   ),
#                            RunsGroup := as.character(get(paste0("group_", i)
#                                                         )
#                                                     )
#                           ]
# 
#     #####  The code below is the same as the code above, but done with dplyr  #####
# 
#          # filter(AllDays_Sorted,
#          #        between(RowNum_OG,
#          #                as.numeric(get(paste0("group_", i, "_start")
#          #                              )
#          #                          ),
#          #                as.numeric(get(paste0("group_", i, "_end")
#          #                              )
#          #                          )
#          #               )
#          #       ) %>% 
#          #   mutate(RunsGroup = as.character(get(paste0("group_", i)
#          #                                     )
#          #                                 )
#          #        )
#         )
# 
#   GroupData[[i]] <- get(paste0("group_", i, "_rows"))
# 
#   message("Processing Group ", i, " of 2,809,062")
# }
# 
# 
# GroupData_df <- rbind.fill(GroupData)
# str(GroupData_df)
# head(GroupData_df)
# tail(GroupData_df)
# # rm(GroupData_df)
# 
# 
# group_1
# group_1_start
# group_1_end
# group_1_rows
# group_2_rows
# group_3_rows
# group_50_rows
# str(group_50_rows)
# group_2809062_rows
# GroupData[[1]]
# GroupData[[50]]
# 
# 
# #####  Testing Area (Below)  #####
# #####  Testing Area (Below)  #####
# #####  Testing Area (Below)  #####
# 
# # head(StopID_Runs$starts, 20)
# # head(AllDays_NewOrder$Stop_ID, 20)
# # 
# # 
# # dat <- as.data.frame(c(1,1,7,7,7,9,6,8,2,2,2,1,1,1,1,1))
# # colnames(dat)[1] <- "dat"
# # r <- rle(dat$dat)
# # dat$run <- rep(r$lengths, r$lengths)
# # dat$runLag <- lag(dat$run)
# # dat$cond <- rep(r$values, r$lengths)
# # dat
# # View(dat)

When consecutive Stop_ID occurrs, only take the first occurrence. This is done because the computation time to select only the record with the longest Dwell_Time for each run was too long (over 5 days).

This is probably less than ideal with regards to Dwell_Time, but should not make much difference for calculations of travel time, speed, etc.


AllDays_FirstStopID <- AllDays_Zips[StopID_Runs$starts, ]

dim(AllDays_Zips)
dim(AllDays_FirstStopID)

nrow(AllDays_Zips) - nrow(AllDays_FirstStopID)

rm(AllDays_Zips, StopID_Runs)
str(AllDays_FirstStopID)

Feature engineering.

Creating new variables.


AllDays_AddVars <- mutate(AllDays_FirstStopID,
                          Odometer_Distance_Mi = Odometer_Distance / 5280, #5,280 feet in 1 mile
                          Dwell_Time2 = as.numeric(Departure_Time - Event_Time),
                          Event_Time_Yr = as.integer(year(Event_Time)),
                          Event_Time_Mth = as.integer(month(Event_Time)),
                          Event_Time_Date = day(Event_Time),
                          Event_Time_Day = wday(Event_Time, label = TRUE),
                          Event_Time_Hr = hour(Event_Time),
                          Event_Time_Min = minute(Event_Time),
                          Event_Time_HrGroup = factor(ifelse(Event_Time_Hr < 3,
                                                             "Group0_2",
                                                      ifelse(Event_Time_Hr < 6,
                                                             "Group3_5",
                                                      ifelse(Event_Time_Hr < 9,
                                                             "Group6_8",
                                                      ifelse(Event_Time_Hr < 12,
                                                             "Group9_11",
                                                      ifelse(Event_Time_Hr < 15,
                                                             "Group12_14",
                                                      ifelse(Event_Time_Hr < 18,
                                                             "Group15_17",
                                                      ifelse(Event_Time_Hr < 21,
                                                             "Group18_20",
                                                      ifelse(Event_Time_Hr < 24,
                                                             "Group21_23"
                                                            )))))))),
                                                         levels = c("Group0_2",
                                                                    "Group3_5",
                                                                    "Group6_8",
                                                                    "Group9_11",
                                                                    "Group12_14",
                                                                    "Group15_17",
                                                                    "Group18_20",
                                                                    "Group21_23"
                                                                   ),
                                                         ordered = TRUE
                                                     )
                         )

rm(AllDays_FirstStopID)
str(AllDays_AddVars)

Function for calculating the distance traveled based on the Haversine formula. Original code from: https://www.r-bloggers.com/great-circle-distance-calculations-in-r/


# Calculates the geodesic distance between two points specified by radian latitude/longitude using the Haversine formula (hf)
# gcd.hf <- function(long1, lat1, long2, lat2) {
#   R <- 6371 # Earth mean radius [km]
#   delta.long <- (long2 - long1)
#   delta.lat <- (lat2 - lat1)
#   a <- sin(delta.lat/2)^2 + cos(lat1) * cos(lat2) * sin(delta.long/2)^2
#   c <- 2 * asin(min(1,sqrt(a)))
#   d = R * c * 0.621371 # 1 km = 0.621371 miles
#   return(d) # Distance in miles
# }

Feature engineering.

Creating more variables. Creating a BusEvent row number for future identification purposes. Then, creating various variables to analyze distance traveled and speed.


AllDays_BusDay <- group_by(AllDays_AddVars,
                           Bus_ID,
                           Event_Time_Date
                          ) %>% 
  mutate(BusDay_EventNum = row_number(),  # used to identify Bus movements on a particular date
         
         Route_Lag1 = lag(Route),  # used in future analyses to identify Route changes
         RouteAlt_Lag1 = lag(RouteAlt),  # used in future analyses to identify RouteAlt (direction) changes
         
         Odometer_Distance_Lag1 = lag(Odometer_Distance),
         
         Latitude_L1 = lag(Latitude),
         Longitude_L1 = lag(Longitude),
         # Lat_Radian = Latitude*pi/180,
         # Long_Radian = Longitude*pi/180,
         # Lat_Radian_L1 = lag(Lat_Radian),
         # Long_Radian_L1 = lag(Long_Radian),
         
         # accounting for potential negative distances
         TravelDistance_Ft = ifelse(Odometer_Distance > Odometer_Distance_Lag1,
                                    Odometer_Distance - Odometer_Distance_Lag1,
                                    NA
                                   ),
         TravelDistance_Mi = TravelDistance_Ft / 5280, #5,280 feet in 1 mile
         
         # TravelDistance_Mi2 = gcd.hf(long1 = Long_Radian_L1,
         #                             lat1 = Lat_Radian_L1,
         #                             long2 = Long_Radian,
         #                             lat2 = Lat_Radian
         #                            ),
         
         TravelDistance_Mi_Hvrs = 
                              # ifelse((is.na(Longitude_L1) | is.na(Latitude_L1)
                              #        ),
                              #        NA,
                              distHaversine(cbind(Longitude_L1, Latitude_L1),
                                            cbind(Longitude, Latitude)
                                           ) * 0.000621371, # 0.000621371 miles = 1 meter
         
         # accounting for potential negative times
         TravelTime_Sec = as.numeric(ifelse(Event_Time > lag(Departure_Time),
                                            Event_Time - lag(Departure_Time),
                                            NA
                                           )
                                    ),
         TravelTime_Hr = TravelTime_Sec / 3600, # 3,600 seconds in 1 hour
         
         # accounting for potential negative or zero travel times
         SpeedAvg_Mph = ifelse(TravelTime_Hr > 0,
                               TravelDistance_Mi / TravelTime_Hr,
                               NA
                              ),
         
         Start_ID = lag(StopID_Clean),
         Start_Desc = lag(Stop_Desc),
         StartStop_ID = ifelse(is.na(Start_ID),
                               paste("NULL", StopID_Clean, sep = "--"),
                               paste(Start_ID, StopID_Clean, sep = "--")
                              )
        ) %>% 
  as.data.frame()


rm(AllDays_AddVars)
str(AllDays_BusDay)

# summary(AllDays_BusDay)

# View(tail(AllDays_BusDay, 50))

Inspecting for issues with StartStop_ID (where the value is either NA or contains NULL). They ONLY exist when BusDay_EventNum = 1 (which is by design). So everything looks OK.


View(group_by(AllDays_BusDay,
              StartStop_ID
             ) %>% 
       summarise(
         Cnt = n()
       ) %>% 
       arrange(desc(Cnt)
              )
    )

View(filter(AllDays_BusDay,
            (is.na(StartStop_ID) |
              str_detect(StartStop_ID, "NULL")
            ) &
              BusDay_EventNum != 1
           )
    )

Stats (quantiles) overall for TravelDistance_Mi.


Quantiles_dt <- AllDays_BusDay %>% 
  mutate(TD_Mi_q2 = quantile(x = TravelDistance_Mi, probs = 0.02, na.rm = TRUE),
         TD_Mi_q98 = quantile(x = TravelDistance_Mi, probs = 0.98, na.rm = TRUE),
         TT_Sec_q2 = quantile(x = TravelTime_Sec, probs = 0.02, na.rm = TRUE),
         TT_Sec_q98 = quantile(x = TravelTime_Sec, probs = 0.98, na.rm = TRUE),
         TT_Hr_q2 = quantile(x = TravelTime_Hr, probs = 0.02, na.rm = TRUE),
         TT_Hr_q98 = quantile(x = TravelTime_Hr, probs = 0.98, na.rm = TRUE)
        ) %>% 
  data.table()


Stats <- Quantiles_dt %>% 
  mutate(TD_Mi_Mean = mean(TravelDistance_Mi, na.rm = TRUE),
         TD_Mi_Mean_F = mean(TravelDistance_Mi[TD_Mi_q2 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_q98],
                             na.rm = TRUE
                            ),
         TD_Mi_Med = median(TravelDistance_Mi, na.rm = TRUE),
         TD_Mi_Med_F = median(TravelDistance_Mi[TD_Mi_q2 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_q98],
                              na.rm = TRUE
                             ),
         TD_Mi_Cnt = sum(!is.na(TravelDistance_Mi)
                        ),
         TD_Mi_Cnt_F = sum(!is.na(TravelDistance_Mi[TD_Mi_q2 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_q98]
                                 )
                          ),
            
         TT_Sec_Mean = mean(TravelTime_Sec, na.rm = TRUE),
         TT_Sec_Mean_F = mean(TravelTime_Sec[TT_Sec_q2 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_q98],
                              na.rm = TRUE
                             ),
         TT_Sec_Med = median(TravelTime_Sec, na.rm = TRUE),
         TT_Sec_Med_F = median(TravelTime_Sec[TT_Sec_q2 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_q98],
                               na.rm = TRUE
                              ),
         TT_Sec_Cnt = sum(!is.na(TravelTime_Sec)
                         ),
         TT_Sec_Cnt_F = sum(!is.na(TravelTime_Sec[TT_Sec_q2 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_q98]
                                   )
                           ),

         TT_Hr_Mean = mean(TravelTime_Hr, na.rm = TRUE),
         TT_Hr_Mean_F = mean(TravelTime_Hr[TT_Hr_q2 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_q98],
                             na.rm = TRUE
                            ),
         TT_Hr_Med = median(TravelTime_Hr, na.rm = TRUE),
         TT_Hr_Med_F = median(TravelTime_Hr[TT_Hr_q2 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_q98],
                              na.rm = TRUE
                             ),
         TT_Hr_Cnt = sum(!is.na(TravelTime_Hr)
                        ),
         TT_Hr_Cnt_F = sum(!is.na(TravelTime_Hr[TT_Hr_q2 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_q98]
                                 )
                          )
        ) %>% 
  data.frame()

rm(AllDays_BusDay)
rm(Quantiles_dt)
str(Stats)
# View(head(Stats, 50))

Stats for StartStop_ID.


Quantiles_SS_dt <- group_by(Stats,
                            StartStop_ID
                           ) %>% 
  mutate(TD_Mi_SS_q5 = quantile(x = TravelDistance_Mi, probs = 0.05, na.rm = TRUE),
         TD_Mi_SS_q95 = quantile(x = TravelDistance_Mi, probs = 0.95, na.rm = TRUE),
         TT_Sec_SS_q5 = quantile(x = TravelTime_Sec, probs = 0.05, na.rm = TRUE),
         TT_Sec_SS_q95 = quantile(x = TravelTime_Sec, probs = 0.95, na.rm = TRUE),
         TT_Hr_SS_q5 = quantile(x = TravelTime_Hr, probs = 0.05, na.rm = TRUE),
         TT_Hr_SS_q95 = quantile(x = TravelTime_Hr, probs = 0.95, na.rm = TRUE)
        ) %>% 
  data.table()


Stats_StSt <- group_by(Quantiles_SS_dt,
                       StartStop_ID
                      ) %>% 
  mutate(TD_Mi_SS_Mean = mean(TravelDistance_Mi, na.rm = TRUE),
         TD_Mi_SS_Mean_F = mean(TravelDistance_Mi[TD_Mi_SS_q5 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_SS_q95],
                                na.rm = TRUE
                               ),
         TD_Mi_SS_Med = median(TravelDistance_Mi, na.rm = TRUE),
         TD_Mi_SS_Med_F = median(TravelDistance_Mi[TD_Mi_SS_q5 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_SS_q95],
                                 na.rm = TRUE
                                ),
         TD_Mi_SS_Cnt = sum(!is.na(TravelDistance_Mi)
                           ),
         TD_Mi_SS_Cnt_F = sum(!is.na(TravelDistance_Mi[TD_Mi_SS_q5 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_SS_q95]
                                    )
                             ),
            
         TT_Sec_SS_Mean = mean(TravelTime_Sec, na.rm = TRUE),
         TT_Sec_SS_Mean_F = mean(TravelTime_Sec[TT_Sec_SS_q5 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_SS_q95],
                                 na.rm = TRUE
                                ),
         TT_Sec_SS_Med = median(TravelTime_Sec, na.rm = TRUE),
         TT_Sec_SS_Med_F = median(TravelTime_Sec[TT_Sec_SS_q5 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_SS_q95],
                                  na.rm = TRUE
                                 ),
         TT_Sec_SS_Cnt = sum(!is.na(TravelTime_Sec)),
         TT_Sec_SS_Cnt_F = sum(!is.na(TravelTime_Sec[TT_Sec_SS_q5 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_SS_q95]
                                     )
                              ),

         TT_Hr_SS_Mean = mean(TravelTime_Hr, na.rm = TRUE),
         TT_Hr_SS_Mean_F = mean(TravelTime_Hr[TT_Hr_SS_q5 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_SS_q95],
                                na.rm = TRUE
                               ),
         TT_Hr_SS_Med = median(TravelTime_Hr, na.rm = TRUE),
         TT_Hr_SS_Med_F = median(TravelTime_Hr[TT_Hr_SS_q5 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_SS_q95],
                                 na.rm = TRUE
                                ),
         TT_Hr_SS_Cnt = sum(!is.na(TravelTime_Hr)),
         TT_Hr_SS_Cnt_F = sum(!is.na(TravelTime_Hr[TT_Hr_SS_q5 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_SS_q95]
                                    )
                             )
        ) %>% 
  data.frame()

rm(Stats)
rm(Quantiles_SS_dt)
str(Stats_StSt)
# View(head(Stats_StSt, 50))

Stats for StartStop_ID with Event_Time_HrGroup.


Quantiles_SSHG_dt <- group_by(Stats_StSt,
                              StartStop_ID,
                              Event_Time_HrGroup
                             ) %>% 
  mutate(TD_Mi_SSHG_q5 = quantile(x = TravelDistance_Mi, probs = 0.05, na.rm = TRUE),
         TD_Mi_SSHG_q95 = quantile(x = TravelDistance_Mi, probs = 0.95, na.rm = TRUE),
         TT_Sec_SSHG_q5 = quantile(x = TravelTime_Sec, probs = 0.05, na.rm = TRUE),
         TT_Sec_SSHG_q95 = quantile(x = TravelTime_Sec, probs = 0.95, na.rm = TRUE),
         TT_Hr_SSHG_q5 = quantile(x = TravelTime_Hr, probs = 0.05, na.rm = TRUE),
         TT_Hr_SSHG_q95 = quantile(x = TravelTime_Hr, probs = 0.95, na.rm = TRUE)
        ) %>% 
  data.table()


Stats_StSt_HrGrp <- group_by(Quantiles_SSHG_dt,
                             StartStop_ID,
                             Event_Time_HrGroup
                            ) %>% 
  mutate(TD_Mi_SSHG_Mean = mean(TravelDistance_Mi, na.rm = TRUE),
         TD_Mi_SSHG_Mean_F = mean(TravelDistance_Mi[TD_Mi_SSHG_q5 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_SSHG_q95],
                                  na.rm = TRUE
                                 ),
         TD_Mi_SSHG_Med = median(TravelDistance_Mi, na.rm = TRUE),
         TD_Mi_SSHG_Med_F = median(TravelDistance_Mi[TD_Mi_SSHG_q5 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_SSHG_q95],
                                   na.rm = TRUE
                                  ),
         TD_Mi_SSHG_Cnt = sum(!is.na(TravelDistance_Mi)
                             ),
         TD_Mi_SSHG_Cnt_F = sum(!is.na(TravelDistance_Mi[TD_Mi_SSHG_q5 <= TravelDistance_Mi & TravelDistance_Mi <= TD_Mi_SSHG_q95]
                                      )
                               ),
            
         TT_Sec_SSHG_Mean = mean(TravelTime_Sec, na.rm = TRUE),
         TT_Sec_SSHG_Mean_F = mean(TravelTime_Sec[TT_Sec_SSHG_q5 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_SSHG_q95],
                                   na.rm = TRUE
                                  ),
         TT_Sec_SSHG_Med = median(TravelTime_Sec, na.rm = TRUE),
         TT_Sec_SSHG_Med_F = median(TravelTime_Sec[TT_Sec_SSHG_q5 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_SSHG_q95],
                                    na.rm = TRUE
                                   ),
         TT_Sec_SSHG_Cnt = sum(!is.na(TravelTime_Sec)),
         TT_Sec_SSHG_Cnt_F = sum(!is.na(TravelTime_Sec[TT_Sec_SSHG_q5 <= TravelTime_Sec & TravelTime_Sec <= TT_Sec_SSHG_q95]
                                       )
                                ),

         TT_Hr_SSHG_Mean = mean(TravelTime_Hr, na.rm = TRUE),
         TT_Hr_SSHG_Mean_F = mean(TravelTime_Hr[TT_Hr_SSHG_q5 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_SSHG_q95],
                                  na.rm = TRUE
                                 ),
         TT_Hr_SSHG_Med = median(TravelTime_Hr, na.rm = TRUE),
         TT_Hr_SSHG_Med_F = median(TravelTime_Hr[TT_Hr_SSHG_q5 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_SSHG_q95],
                                   na.rm = TRUE
                                  ),
         TT_Hr_SSHG_Cnt = sum(!is.na(TravelTime_Hr)),
         TT_Hr_SSHG_Cnt_F = sum(!is.na(TravelTime_Hr[TT_Hr_SSHG_q5 <= TravelTime_Hr & TravelTime_Hr <= TT_Hr_SSHG_q95]
                                      )
                               )
        ) %>% 
  data.frame()

rm(Stats_StSt)
rm(Quantiles_SSHG_dt)
str(Stats_StSt_HrGrp)
# View(head(Stats_StSt_HrGrp, 50))

Feature engineering.

Creating a BusEventRoute row number, and a RouteAlt_Lag1 indicator for future identification purposes.


# rm(Quantiles_dt)
# rm(Quantiles_SS_dt)
# rm(AllDays_BusDay)
# rm(Quantiles_SSHG_dt)
# rm(Stats_StSt)

# AllDays_BusDayRoute <- group_by(Stats_StSt_HrGrp,
#                                 Bus_ID,
#                                 Event_Time_Date,
#                                 Route
#                                ) %>% 
#   mutate(RouteAlt_Lag2 = lag(RouteAlt)  # used in future analyses to identify RouteAlt (direction) changes
#          
#          # Odometer_Distance_Lag1 = lag(Odometer_Distance),
#          # 
#          # # accounting for potential negative distances
#          # TravelDistance_Ft = ifelse(Odometer_Distance >= Odometer_Distance_Lag1,
#          #                            Odometer_Distance - Odometer_Distance_Lag1,
#          #                            NA
#          #                           ),
#          # TravelDistance_Mi = TravelDistance_Ft / 5280, #5,280 feet in 1 mile
#          # 
#          # # accounting for potential negative times
#          # TravelTime_Sec = as.numeric(ifelse(Event_Time >= lag(Departure_Time),
#          #                                    Event_Time - lag(Departure_Time),
#          #                                    NA
#          #                                   )
#          #                            ),
#          # TravelTime_Hr = TravelTime_Sec / 3600, # 3,600 seconds in 1 hour
#          # 
#          # # accounting for potential negative or zero travel times
#          # SpeedAvg_Mph = ifelse(TravelTime_Hr > 0,
#          #                       TravelDistance_Mi / TravelTime_Hr,
#          #                       NA
#          #                      )
#         ) %>% 
#   data.frame()
# 
# rm(Stats_StSt_HrGrp)
# str(AllDays_BusDayRoute)

Feature engineering.

Calculating a variable to know if the RouteAlt changed. Could be useful in helping identifying weirdness in calculated distances and speeds.


# rm(Stats_StSt_HrGrp)

AllDays_DirChange <- Stats_StSt_HrGrp %>%  # AllDays_BusDayRoute %>% 
  mutate(RteChange = ifelse(Route == Route_Lag1,
                            "Same",
                            "Change"
                           ),
         RteChange2 = factor(ifelse(is.na(RteChange),
                                    "Change",
                                    RteChange
                                   )
                            ),
         DirChange = ifelse(RouteAlt == RouteAlt_Lag1,
                            "Same",
                            "Change"
                           ),
         DirChange2 = factor(ifelse(is.na(DirChange),
                                    "Change",
                                    DirChange
                                   )
                            )
        )

# rm(AllDays_BusDayRoute)
rm(Stats_StSt_HrGrp)
str(AllDays_DirChange)

View(filter(AllDays_DirChange,
            between(RowNum_OG, 2570060, 2570080)
           ) %>% 
       select(-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
             )
    )

Re-ordering the variables to ease with comprehension.


AllDays_NewOrder <-  select(AllDays_DirChange,
                            RowNum_OG,
                            UniqueLatLng,
                            group,
                            StartStop_ID,
                            BusDay_EventNum,
                            Bus_ID,
                            Route,
                            RteChange2,
                            RouteAlt,
                            # RouteAlt_Lag1,
                            DirChange2,
                            Route_Direction,
                            Stop_Sequence,
                            Start_ID,
                            Start_Desc,
                            # Stop_ID,
                            StopID_Clean,
                            StopID_Indicator,
                            Stop_Desc,
                            countryCode,
                            Stop_State,
                            Stop_County,
                            Stop_City,
                            Stop_Zip,
                            Event_Type,
                            Event_Description,
                            Event_Time_Yr,
                            Event_Time_Mth,
                            Event_Time_Date,
                            Event_Time_Day,
                            Event_Time_Hr,
                            Event_Time_HrGroup,
                            Event_Time_Min,
                            Event_Time,
                            Departure_Time,
                            Dwell_Time,
                            Dwell_Time2,
                            Delta_Time,
                            Latitude,
                            Longitude,
                            Heading,
                            Odometer_Distance,
                            Odometer_Distance_Lag1,
                            Odometer_Distance_Mi,
                            TravelDistance_Ft,
                            TravelDistance_Mi,
                            TravelDistance_Mi_Hvrs,
                            TD_Mi_q2,
                            TD_Mi_q98,
                            TD_Mi_SS_q5,
                            TD_Mi_SS_q95,
                            TD_Mi_SSHG_q5,
                            TD_Mi_SSHG_q95,
                            TD_Mi_Mean,
                            TD_Mi_Mean_F,
                            TD_Mi_SS_Mean,
                            TD_Mi_SS_Mean_F,
                            TD_Mi_SSHG_Mean,
                            TD_Mi_SSHG_Mean_F,
                            TD_Mi_Med,
                            TD_Mi_Med_F,
                            TD_Mi_SS_Med,
                            TD_Mi_SS_Med_F,
                            TD_Mi_SSHG_Med,
                            TD_Mi_SSHG_Med_F,
                            TD_Mi_Cnt,
                            TD_Mi_Cnt_F,
                            TD_Mi_SS_Cnt,
                            TD_Mi_SS_Cnt_F,
                            TD_Mi_SSHG_Cnt,
                            TD_Mi_SSHG_Cnt_F,
                            TravelTime_Sec,
                            TT_Sec_q2,
                            TT_Sec_q98,
                            TT_Sec_SS_q5,
                            TT_Sec_SS_q95,
                            TT_Sec_SSHG_q5,
                            TT_Sec_SSHG_q95,
                            TT_Sec_Mean,
                            TT_Sec_Mean_F,
                            TT_Sec_SS_Mean,
                            TT_Sec_SS_Mean_F,
                            TT_Sec_SSHG_Mean,
                            TT_Sec_SSHG_Mean_F,
                            TT_Sec_Med,
                            TT_Sec_Med_F,
                            TT_Sec_SS_Med,
                            TT_Sec_SS_Med_F,
                            TT_Sec_SSHG_Med,
                            TT_Sec_SSHG_Med_F,
                            TT_Sec_Cnt,
                            TT_Sec_Cnt_F,
                            TT_Sec_SS_Cnt,
                            TT_Sec_SS_Cnt_F,
                            TT_Sec_SSHG_Cnt,
                            TT_Sec_SSHG_Cnt_F,
                            TravelTime_Hr,
                            TT_Hr_q2,
                            TT_Hr_q98,
                            TT_Hr_SS_q5,
                            TT_Hr_SS_q95,
                            TT_Hr_SSHG_q5,
                            TT_Hr_SSHG_q95,
                            TT_Hr_Mean,
                            TT_Hr_Mean_F,
                            TT_Hr_SS_Mean,
                            TT_Hr_SS_Mean_F,
                            TT_Hr_SSHG_Mean,
                            TT_Hr_SSHG_Mean_F,
                            TT_Hr_Med,
                            TT_Hr_Med_F,
                            TT_Hr_SS_Med,
                            TT_Hr_SS_Med_F,
                            TT_Hr_SSHG_Med,
                            TT_Hr_SSHG_Med_F,
                            TT_Hr_Cnt,
                            TT_Hr_Cnt_F,
                            TT_Hr_SS_Cnt,
                            TT_Hr_SS_Cnt_F,
                            TT_Hr_SSHG_Cnt,
                            TT_Hr_SSHG_Cnt_F,
                            SpeedAvg_Mph
                           )

rm(AllDays_DirChange)
str(select(AllDays_NewOrder,
           -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
          )
   )
str(AllDays_NewOrder)

# View(head(AllDays_NewOrder, 500))
# View(tail(AllDays_NewOrder, 500))

Summarizing the data to help spot anomolies.


View(group_by(AllDays_NewOrder,
              Stop_City) %>% 
       summarise(Cnt_Num = n(),
                 Cnt_Pct = 100*Cnt_Num / (nrow(AllDays_NewOrder)
                                         )
                ) %>% 
       arrange(desc(Cnt_Num))
)

summary(AllDays_NewOrder)

Investigation of TravelDistance_Mi.

View(TravDistMi_Pctiles): 99% of TravelDistance_Mi are about 1 mile or less…but some weird TravelDistance_Mi values (e.g., 584 miles traveled) exist.


TravDistMi_Ntile <- as.data.frame(AllDays_NewOrder$TravelDistance_Mi) %>% 
  mutate(#Pctile = ntile(AllDays_NewOrder$TravelDistance_Mi, 100),
         #MinR = min_rank(AllDays_NewOrder$TravelDistance_Mi),
         PctR = percent_rank(AllDays_NewOrder$TravelDistance_Mi),
         PctR_Round = round(PctR, 2)
        ) 

colnames(TravDistMi_Ntile)[1] <- "TravelDistance_Mi"
# str(TravDistMi_Ntile)

TravDistMi_Ntile_Rows <- nrow(TravDistMi_Ntile)

# View(tail(TravDistMi_Ntile, 500))


TravDistMi_Pctiles <- group_by(TravDistMi_Ntile,
                               PctR_Round
                              ) %>% 
  summarise(
    MinTravDistMiAtPctile = min(TravelDistance_Mi),
    CntsAtPctile = n(),
    PctsAtPctile = CntsAtPctile / TravDistMi_Ntile_Rows
  ) %>% 
  mutate(CumSumPAtP = cumsum(PctsAtPctile)
        )

rm(TravDistMi_Ntile)
rm(TravDistMi_Ntile_Rows)

View(TravDistMi_Pctiles)
TravDistMi_Pctiles

Investigation of TravelDistance_Mi.

Why are some TravelDistance_Mi “NA”? It looks like partially because the records are the first trip of the day (for that bus), so I purposefully set the distance to “NA”. Another reason is due to the odometer recording a value less than the previous odometer recording. In most cases, I have no explanation for this - though I have observed about 67% of all instances where TravelDistance_Mi is NA (other than because it’s the first record of the day) are instances where DirChange2 is “Change”. This is weird and should be asked to WMATA.


# View(head(AllDays_NewOrder, 500))

View(filter(AllDays_NewOrder,
            BusDay_EventNum != 1 # When BusDay_EventNum == 1, TravelDistance_Mi is NA by design (don't want to calculate distance based on yesterday's position)
           ) %>% 
       group_by(StartStop_ID) %>% 
       summarise(Cnts = sum(is.na(TravelDistance_Mi)
                           )
                ) %>% 
       arrange(desc(Cnts)
              )
    )

View(filter(AllDays_NewOrder,
            StartStop_ID == "1000245--1000211"
           ) %>% 
       select(RowNum_OG,
              StartStop_ID,
              Event_Time,
              Event_Time_HrGroup,
              Bus_ID,
              TravelDistance_Mi,
              TravelDistance_Mi_Hvrs,
              TD_Mi_SS_Mean,
              TD_Mi_SS_Mean_F,
              TD_Mi_SSHG_Mean,
              TD_Mi_SSHG_Mean_F,
              TD_Mi_SS_Med,
              TD_Mi_SS_Med_F,
              TD_Mi_SSHG_Med,
              TD_Mi_SSHG_Med_F,
              TD_Mi_SS_Cnt,
              TD_Mi_SS_Cnt_F,
              TD_Mi_SSHG_Cnt,
              TD_Mi_SSHG_Cnt_F
              ) %>% 
       mutate(Ratio_MeanToHvrs = TD_Mi_SS_Mean / TravelDistance_Mi_Hvrs) %>% 
       arrange(Event_Time)
    )

View(filter(AllDays_NewOrder,
            is.na(TravelDistance_Mi)
           )
    )

# These records are NA becuase the record is the first record of the day (the Event_Time_Date)
View(filter(AllDays_NewOrder,
            between(RowNum_OG, 326, 346) | # 336
              between(RowNum_OG, 591, 611) | # 601
              between(RowNum_OG, 845, 865) # 855
           )
    )

Investigation of TravelDistance_Mi.

These records are NA becuase the current record odometer is less than the previous record odometer. Theoretically, this should NOT happen. Me: it appears that about 67% of all instances where TravelDistance_Mi is NA (other than because it’s th first record of the day) are instances where DirChange2 is “Change”. This is weird and should be asked to WMATA.


View(filter(AllDays_NewOrder,
            between(RowNum_OG, 194, 214) | # 204
              between(RowNum_OG, 440, 460) | # 450
              between(RowNum_OG, 478, 498) | # 488
              between(RowNum_OG, 510, 530) # 520
           )
    )

TestTable <- filter(AllDays_NewOrder,
                    BusDay_EventNum != 1
                   ) %>% 
  mutate(TravelDistance_NA = as.factor(ifelse(is.na(TravelDistance_Mi),
                                              "True",
                                              "False"
                                             )
                                      )
        ) %>%
  group_by(DirChange2, TravelDistance_NA) %>%
  summarise(TravDistMi_NACnts = n()
           )

# TestTable

TestTable_Spread <- as.data.frame(spread(TestTable,
                                         TravelDistance_NA,
                                         TravDistMi_NACnts
                                        )
                                 ) %>% 
  select(False,
         True
        )

row.names(TestTable_Spread) <- c("Change", "Same")
# str(TestTable_Spread)
# TestTable_Spread

prop.table(as.table(as.matrix(TestTable_Spread)
                   ),
           1
          )

prop.table(as.table(as.matrix(TestTable_Spread)
                   ),
           2
          )

Investigation of TravelDistance_Mi.

Let’s look at just the TravelDistance_Mi values that are NOT “NA”.


rm(TestTable, TestTable_Spread)

TravelDistance_Mi_NoNA <- filter(AllDays_NewOrder,
                                 # TravelDistance_Mi != 0 &
                                 !is.na(TravelDistance_Mi)
                                )

dim(AllDays_NewOrder)
dim(TravelDistance_Mi_NoNA)
nrow(AllDays_NewOrder) - nrow(TravelDistance_Mi_NoNA)

str(TravelDistance_Mi_NoNA)
summary(TravelDistance_Mi_NoNA)

Investigation of TravelDistance_Mi.

Let’s plot just the TravelDistance_Mi values that are NOT “NA”.


TravDistMi_HistDen <- ggplot(select(TravelDistance_Mi_NoNA,
                                    TravelDistance_Mi
                                   ),
                             aes(x = TravelDistance_Mi,
                                 y = ..density..
                                )
                            ) +
  geom_histogram(binwidth = 0.05, fill = "lightblue", colour = "grey60", size = 0.2) +
  geom_line(stat = "density", colour = "red") +
  coord_cartesian(xlim = c(0, 1.5), ylim = c(0, 4.0)
                 ) +
  labs(title = "Variation in Distance Between Stops",
       x = "Travel Distance (miles)",
       y = "Density"
      )

TravDistMi_HistDen

Investigation of TravelDistance_Mi.

Looking at the extremely large TravelDistance_Mi values. Some (aprox 27%) of TravelDistance_Mi values > 1 mile are when the DirChange2 changes…but what about the other ~73%?


rm(TravelDistance_Mi_NoNA)

# examples of weirdly large TravelDistance_Mi
View(filter(AllDays_NewOrder,
            TravelDistance_Mi > 1.1587121212 # 1.1587121212 is the 99th percentile
           ) %>% 
       arrange(desc(TravelDistance_Mi)
              )
    )


# Why are these extremes?  Airports?  Bus collection points?
View(filter(AllDays_NewOrder,
              between(RowNum_OG, 494044, 494064) | # 494054
              between(RowNum_OG, 494273, 494293) | # 494283
              between(RowNum_OG, 494626, 494646) | # 494636
              between(RowNum_OG, 1610156, 1610176) | # 1610166
              between(RowNum_OG, 2073074, 2073094) # 2073084
           )
    )

# Before Removing Runs
# View(filter(AllDays_Sorted,
#             between(RowNum_OG, 494044, 494064) | # 494054
#               between(RowNum_OG, 494273, 494293) | # 494283
#               between(RowNum_OG, 494626, 494646) | # 494636
#               between(RowNum_OG, 1610156, 1610176) | # 1610166
#               between(RowNum_OG, 2073074, 2073094) # 2073084
#            )
#     )

# After Removing Runs
# View(filter(AllDays_FirstStopID,
#             between(RowNum_OG, 494044, 494064) | # 494054
#               between(RowNum_OG, 494273, 494293) | # 494283
#               between(RowNum_OG, 494626, 494646) | # 494636
#               between(RowNum_OG, 1610156, 1610176) | # 1610166
#               between(RowNum_OG, 2073074, 2073094) # 2073084
#            )
#     )

Investigation of TravelDistance_Mi.

Any relation with DirChange2? Doesn’t look as if this is so.


ExtremeTravDist <- filter(AllDays_NewOrder,
                          !is.na(TravelDistance_Mi)
                         ) %>% 
  mutate(TravDist_Extreme = ifelse(TravelDistance_Mi > 1.1587121212, # 1.1587121212 is the 99th percentile
                                   "True",
                                   "False"
                                  )
                          ) %>% 
  group_by(DirChange2, TravDist_Extreme) %>% 
  summarise(TravDistMI_ExtCnts = n()
           )

# ExtremeTravDist


ExtremeTravDist_Spread <- as.data.frame(spread(ExtremeTravDist,
                                               TravDist_Extreme,
                                               TravDistMI_ExtCnts
                                              )
                                       ) %>% 
  select(False,
         True
        )

row.names(ExtremeTravDist_Spread) <- c("Change", "Same")
# str(ExtremeTravDist_Spread)
# ExtremeTravDist_Spread

prop.table(as.table(as.matrix(ExtremeTravDist_Spread)
                   ),
           1
          )

prop.table(as.table(as.matrix(ExtremeTravDist_Spread)
                   ),
           2
          )

Investigation of TravelDistance_Mi.

Looking at specific buses and StartStop_ID.


rm(ExtremeTravDist, ExtremeTravDist_Spread)

View(arrange(group_by(AllDays_NewOrder,
                      Bus_ID
                     ) %>% 
               summarise(DistTrav_Mean = mean(TravelDistance_Mi, na.rm = TRUE),
                         DistTrav_Med = median(TravelDistance_Mi, na.rm = TRUE)
                        ),
             desc(DistTrav_Med)
            )
    )


# example of extremely small TravelDistance_Mi values (looks like the odometer wasn't functioning)
View(filter(AllDays_NewOrder,
            Bus_ID == 6111 |
              Bus_ID == 7201 |
              Bus_ID == 8058
           ) %>% 
       arrange(Bus_ID, Event_Time)
    )


View(arrange(group_by(AllDays_NewOrder,
                      StartStop_ID
                     ) %>% 
               summarise(DistTrav_Mean = mean(TravelDistance_Mi, na.rm = TRUE),
                         DistTrav_Med = median(TravelDistance_Mi, na.rm = TRUE)
                        ),
             desc(DistTrav_Med)
            )
    )

# example of extremely large TravelDistance_Mi values...no idea why...
View(filter(AllDays_NewOrder,
            StartStop_ID == "1003665--12" |
              StartStop_ID == "1003665--5001925" |
              StartStop_ID == "3001038--3002565"
           ) %>% 
       arrange(StartStop_ID, Event_Time)
    )

Investigation of TravelDistance_Mi & TravelDistance_Mi_New.

If TravelDisntace_Mi is below the 5th percentile for that StartStop_ID, or if TravelDisntace_Mi is above the 95th percentile for that StartStop_ID, or if TravelDistance_Mi is NA (when the BusDay_EventNum !=1), consider this an outlier. In this case, replace the value with the mean for that StartStop_ID and HourGroup (TD_Mi_SSHG_Mean_F), or if there are not enough values at the HourGroup level, replace it with the mean for that StartStop_ID.


# View(tail(AllDays_NewOrder, 500))

AllDays_NewTravelDist <- 
  mutate(AllDays_NewOrder,
         TravelDistance_Mi_New = ifelse(!is.na(TravelDistance_Mi) & 
                                          (TravelDistance_Mi < TD_Mi_SSHG_q5 |
                                             TravelDistance_Mi > TD_Mi_SSHG_q95
                                          ) &
                                          TD_Mi_SSHG_Cnt_F >= 20,
                                        TD_Mi_SSHG_Mean_F,
                                 ifelse(!is.na(TravelDistance_Mi) & 
                                          (TravelDistance_Mi < TD_Mi_SSHG_q5 |
                                             TravelDistance_Mi > TD_Mi_SSHG_q95
                                          ) &
                                          TD_Mi_SSHG_Cnt_F < 20 &
                                          TD_Mi_SS_Cnt_F >= 20,
                                        TD_Mi_SS_Mean_F,
                                 ifelse(!is.na(TravelDistance_Mi) & 
                                          (TravelDistance_Mi < TD_Mi_SSHG_q5 |
                                             TravelDistance_Mi > TD_Mi_SSHG_q95
                                          ) &
                                          TD_Mi_SS_Cnt_F < 20 &
                                          TD_Mi_SS_Cnt >= 20,
                                        TD_Mi_SS_Mean,
                                 ifelse(is.na(TravelDistance_Mi) &
                                          BusDay_EventNum != 1 &
                                          TravelDistance_Mi_Hvrs != 0,
                                        TravelDistance_Mi_Hvrs,
                                 ifelse(is.na(TravelDistance_Mi) &
                                          BusDay_EventNum != 1 &
                                          TravelDistance_Mi_Hvrs == 0,
                                        TD_Mi_SS_Mean,
                                        TravelDistance_Mi
                                       ))))),
         TravelDistance_Mi_New_Label = 
           factor(ifelse(!is.na(TravelDistance_Mi) &
                           (TravelDistance_Mi < TD_Mi_SSHG_q5 |
                              TravelDistance_Mi > TD_Mi_SSHG_q95
                           ) &
                           TD_Mi_SSHG_Cnt_F >= 20,
                         "TD_Mi_SSHG_Mean_F",
                  ifelse(!is.na(TravelDistance_Mi) &
                           (TravelDistance_Mi < TD_Mi_SSHG_q5 |
                              TravelDistance_Mi > TD_Mi_SSHG_q95
                           ) &
                           TD_Mi_SSHG_Cnt_F < 20 &
                           TD_Mi_SS_Cnt_F >= 20,
                         "TD_Mi_SS_Mean_F",
                  ifelse(!is.na(TravelDistance_Mi) &
                           (TravelDistance_Mi < TD_Mi_SSHG_q5 |
                              TravelDistance_Mi > TD_Mi_SSHG_q95
                           ) &
                           TD_Mi_SS_Cnt_F < 20 &
                           TD_Mi_SS_Cnt >= 20,
                         "TD_Mi_SS_Mean",
                  ifelse(is.na(TravelDistance_Mi) &
                           BusDay_EventNum != 1 &
                           TravelDistance_Mi_Hvrs != 0,
                         "TravelDistance_Mi_Hvrs",
                  ifelse(is.na(TravelDistance_Mi) &
                           BusDay_EventNum != 1 &
                           TravelDistance_Mi_Hvrs == 0,
                         "TD_Mi_SS_Mean",
                         "TravelDistance_Mi"
                        )))))
                 ),
         TravelDistance_Mi_NewHvrs = ifelse(!is.na(TravelDistance_Mi_Hvrs) &
                                              TravelDistance_Mi_Hvrs != 0 &
                                              (TravelDistance_Mi_New < TD_Mi_q2 |
                                                 TravelDistance_Mi_New > TD_Mi_q98
                                              ),
                                            TravelDistance_Mi_Hvrs,
                                            TravelDistance_Mi_New
                                           ),
         TravelDistance_Mi_NewHvrs_Label =
           factor(ifelse(!is.na(TravelDistance_Mi_Hvrs) &
                           TravelDistance_Mi_Hvrs != 0 &
                           (TravelDistance_Mi_New < TD_Mi_q2 |
                              TravelDistance_Mi_New > TD_Mi_q98
                           ),
                         "TravelDistance_Mi_Hvrs",
                         as.character(TravelDistance_Mi_New_Label)
                        )
                 ),
         SpeedAvg_Mph_NewHvrs = TravelDistance_Mi_NewHvrs / TravelTime_Hr
        )

rm(AllDays_NewOrder)
str(AllDays_NewTravelDist)

Investigation of TravelDistance_Mi & TravelDistance_Mi_Hvrs & TravelDistance_Mi_New.

Quick summary and then correlation calculation.


# 38 rows meet this criteria anymore  --  appears to be the case when both the Lat Long calculations, and the TravelDistance calculations did not function properly.
View(filter(AllDays_NewTravelDist,
            is.na(TravelDistance_Mi_New) &
              BusDay_EventNum != 1
           )
    )

View(AllDays_NewTravelDist %>% 
       arrange(desc(TravelDistance_Mi_New)) %>% 
       head(500)
    )

summary(select(AllDays_NewTravelDist,
               TravelDistance_Mi,
               TravelDistance_Mi_Hvrs,
               TravelDistance_Mi_New,
               TravelDistance_Mi_NewHvrs
              )
       )

summary(select(filter(AllDays_NewTravelDist,
                      BusDay_EventNum != 1
                     ),
               TravelDistance_Mi,
               TravelDistance_Mi_Hvrs,
               TravelDistance_Mi_New,
               TravelDistance_Mi_NewHvrs
              )
       )


cor(select(AllDays_NewTravelDist,
           TravelDistance_Mi,
           TravelDistance_Mi_Hvrs,
           TravelDistance_Mi_New,
           TravelDistance_Mi_NewHvrs
          ),
    use = "pairwise.complete.obs"
  )

Investigation of TravelDistance_Mi_NewHvrs_Label & TravelDistance_Mi_NewHvrs_Label.

Show how the labels changed.


group_by(AllDays_NewTravelDist,
         TravelDistance_Mi_New_Label,
         TravelDistance_Mi_NewHvrs_Label
        ) %>% 
  summarise(CntNum = n(),
            CntPct = format(CntNum / nrow(AllDays_NewTravelDist),
                            scientific = 9999
                           )
           ) %>% 
  arrange(desc(CntPct)
         )

Investigation of TravelDistance_Mi & TravelDistance_Mi_Hvrs & TravelDistance_Mi_New.

Graphing the two methods of calculating TravelDistance_Mi.

First, let’s get create a function to plot the liner model equation.


lm_eqn <- function(df, y, x){
  m <- lm(y ~ x, df)
  
  l <- list(a = format(coef(m)[1], digits = 2),
            b = format(abs(coef(m)[2]), digits = 2),
            s1 = ifelse(test = coef(m)[2] > 0,
                        yes = "+",
                        no = "-"
                       ),
            r2 = format(summary(m)$r.squared,
                        digits = 3
                       )
           )
  
  eq <- substitute(italic(y) == a~~s1~~b %.% italic(x)*","~~italic(r)^2~"="~r2,
                   l
                  )
  
  as.character(as.expression(eq)
              )             
}

Investigation of TravelDistance_Mi & TravelDistance_Mi_NewHvrs.

Scatter plot (using a 10% sample to making plotting time faster and to reduce un-needed data in the “same” splot).


set.seed(123456789)
AllDays_NewTravelDist_10Pct <- filter(AllDays_NewTravelDist,
                                      !is.na(TravelDistance_Mi_NewHvrs) &
                                        !is.na(TravelDistance_Mi)
                                     ) %>% 
  rename(DistMethod = TravelDistance_Mi_NewHvrs_Label) %>% 
  sample_frac(0.1)


TravDist_MiVsCalc <- ggplot(select(AllDays_NewTravelDist_10Pct,
                                   TravelDistance_Mi_NewHvrs,
                                   TravelDistance_Mi,
                                   DistMethod
                                  ),
                            aes(x = TravelDistance_Mi,
                                y = TravelDistance_Mi_NewHvrs,
                                colour = DistMethod
                               )
                           ) +
  scale_colour_manual(values = c("red","blue", "green", "orange", "black")
                     ) +
  geom_point(shape = 1, alpha = 0.5) +
  scale_shape(solid = FALSE) +
  geom_smooth(method = "lm", colour = "blue") +
  geom_abline(intercept = 0, slope = 1, colour = "red") +
  coord_cartesian(xlim = c(0, 1.5), ylim = c(0, 1.5)
                 ) +
  scale_x_continuous(breaks = seq(0, 1.5, 0.25)
                    ) +
  scale_y_continuous(breaks = seq(0, 1.5, 0.25)
                    ) +
  theme(legend.position = "bottom", #c(0.85, 0.40),
        legend.text = element_text(size = 6)
       ) +
  annotate(label = lm_eqn(df = AllDays_NewTravelDist_10Pct,
                          x = AllDays_NewTravelDist_10Pct$TravelDistance_Mi,
                          y = AllDays_NewTravelDist_10Pct$TravelDistance_Mi_NewHvrs
                         ),
           # x = 62,
           # y = 20,
           x = 0.70,
           y = 0.00,
           geom = "text",
           size = 3,
           colour = "blue",
           parse = TRUE
          ) +
  annotate(label = "Reference Line (slope = 1)",
           # x = 16,
           # y = 30,
           x = 0.80,
           y = 1.05,
           geom = "text",
           size = 3,
           colour = "red"
          ) +
  labs(title = "TravelDistance_Mi vs. TravelDistance_Mi_NewHvrs",
       x = "TravelDistance_Mi",
       y = "TravelDistance_Mi_NewHvrs"
      )
# +
#   geom_jitter()

TravDist_MiVsCalc

Investigation of TravelDistance_Mi & TravelDistance_Mi_Hvrs & TravelDistance_Mi_New.

Graphing test with rbokeh.


TravDist_MiVsCalc_Bokeh <- figure(data = select(AllDays_NewTravelDist_10Pct,
                                                TravelDistance_Mi_NewHvrs,
                                                TravelDistance_Mi,
                                                DistMethod
                                               ),
                                  xlim = c(0, 1.5),
                                  ylim = c(0, 1.5),
                                  legend_location = "bottom_right"
                                 ) %>% 
  ly_points(x = TravelDistance_Mi,
            y = TravelDistance_Mi_NewHvrs,
            color = DistMethod,
            hover = c(TravelDistance_Mi_NewHvrs, TravelDistance_Mi, DistMethod)
           ) %>% 
  ly_abline(a = 0, b = 1, color = "red")

TravDist_MiVsCalc_Bokeh

Investigation of TravelDistance_Mi_New.

Calculating the minimum TravelDistance_Mi_New value at each percentile.


rm(TravDist_MiVsCalc_Bokeh)
rm(AllDays_NewTravelDist_10Pct)


summary(select(AllDays_NewTravelDist,
               TravelDistance_Mi,
               TravelDistance_Mi_Hvrs,
               TravelDistance_Mi_New,
               TravelDistance_Mi_NewHvrs
              )
       )

summary(select(filter(AllDays_NewTravelDist,
                      BusDay_EventNum != 1
                     ),
               TravelDistance_Mi,
               TravelDistance_Mi_Hvrs,
               TravelDistance_Mi_New,
               TravelDistance_Mi_NewHvrs
              )
       )


TravDistMiN_Ntile <- as.data.frame(select(AllDays_NewTravelDist,
                                          StartStop_ID,
                                          TravelDistance_Mi_New_Label,
                                          # TravelDistance_Mi_NewHvrs_Label,
                                          TravelDistance_Mi_New
                                          # TravelDistance_Mi_NewHvrs
                                         )
                                  ) %>% 
  mutate(PctR_N = percent_rank(AllDays_NewTravelDist$TravelDistance_Mi_New),
         # PctR_H = percent_rank(AllDays_NewTravelDist$TravelDistance_Mi_NewHvrs),
         PctR_Round_N = round(PctR_N, 2)
         # PctR_Round_H = round(PctR_H, 2)
        ) 

# str(TravDistMiN_Ntile)
# View(head(TravDistMiN_Ntile, 500))

TravDistMiN_Ntile_Rows <- nrow(TravDistMiN_Ntile)

# View(tail(TravDistMiN_Ntile, 500))


TravDistMiN_Pctiles <- group_by(TravDistMiN_Ntile,
                                PctR_Round_N
                               ) %>% 
  summarise(
    MinTDMiAtPctile_N = min(TravelDistance_Mi_New),
    # MinTDMiAtPctile_H = min(TravelDistance_Mi_NewHvrs),
    CntsAtPctile_N = sum(!is.na(TravelDistance_Mi_New)),
    # CntsAtPctile_H = sum(!is.na(TravelDistance_Mi_NewHvrs)),
    PctsAtPctile_N = CntsAtPctile_N / TravDistMiN_Ntile_Rows
    # PctsAtPctile_H = CntsAtPctile_H / TravDistMiN_Ntile_Rows
  ) %>% 
  mutate(CumSumPAtP_N = cumsum(PctsAtPctile_N)
         # CumSumPAtP_H = cumsum(PctsAtPctile_H)
        )

# View(TravDistMiN_Pctiles)
TravDistMiN_Pctiles

Investigation of TravelDistance_Mi_NewHvrs

Calculating the minimum TravelDistance_Mi_NewHvrs value at each percentile.


TravDistMiH_Ntile <- as.data.frame(select(AllDays_NewTravelDist,
                                          StartStop_ID,
                                          # TravelDistance_Mi_New_Label,
                                          TravelDistance_Mi_NewHvrs_Label,
                                          # TravelDistance_Mi_New,
                                          TravelDistance_Mi_NewHvrs
                                         )
                                  ) %>% 
  mutate(# PctR_N = percent_rank(AllDays_NewTravelDist$TravelDistance_Mi_New),
         PctR_H = percent_rank(AllDays_NewTravelDist$TravelDistance_Mi_NewHvrs),
         # PctR_Round_N = round(PctR_N, 2),
         PctR_Round_H = round(PctR_H, 2)
        ) 

# str(TravDistMiH_Ntile)
# View(head(TravDistMiH_Ntile, 500))

TravDistMiH_Ntile_Rows <- nrow(TravDistMiH_Ntile)

# View(tail(TravDistMiH_Ntile, 500))


TravDistMiH_Pctiles <- group_by(TravDistMiH_Ntile,
                                PctR_Round_H
                               ) %>% 
  summarise(
    # MinTDMiAtPctile_N = min(TravelDistance_Mi_New),
    MinTDMiAtPctile_H = min(TravelDistance_Mi_NewHvrs),
    # CntsAtPctile_N = sum(!is.na(TravelDistance_Mi_New)),
    CntsAtPctile_H = sum(!is.na(TravelDistance_Mi_NewHvrs)),
    # PctsAtPctile_N = CntsAtPctile_N / TravDistMiH_Ntile_Rows,
    PctsAtPctile_H = CntsAtPctile_H / TravDistMiH_Ntile_Rows
  ) %>% 
  mutate(# CumSumPAtP_N = cumsum(PctsAtPctile_N),
         CumSumPAtP_H = cumsum(PctsAtPctile_H)
        )

# View(TravDistMiH_Pctiles)
TravDistMiH_Pctiles

Join TravDistMiH_Pctiles, TravDistMiN_Pctiles, and TravDistMi_Pctiles.

~11% of rides are still showing as less than 0.1 miles of TravelDistance_Mi_NewHvrs.


rm(TravDistMiN_Ntile_Rows, TravDistMiH_Ntile_Rows, TravDistMiN_Ntile, TravDistMiH_Ntile)


# View(TravDistMi_Pctiles)
# View(TravDistMiN_Pctiles)
# View(TravDistMiH_Pctiles)

TravDistMi_Pctiles_All <- inner_join(x = TravDistMi_Pctiles,
                                     y = TravDistMiN_Pctiles,
                                     by = c("PctR_Round" = "PctR_Round_N")
                                    ) %>% 
  inner_join(y = TravDistMiH_Pctiles,
             by = c("PctR_Round" = "PctR_Round_H")
            ) %>% 
  select(PctR_Round,
         MinTravDistMiAtPctile,
         MinTDMiAtPctile_N,
         MinTDMiAtPctile_H,
         CntsAtPctile,
         CntsAtPctile_N,
         CntsAtPctile_H,
         PctsAtPctile,
         PctsAtPctile_N,
         PctsAtPctile_H,
         CumSumPAtP,
         CumSumPAtP_N,
         CumSumPAtP_H
         )

# str(TravDistMi_Pctiles_All)

rm(TravDistMi_Pctiles, TravDistMiN_Pctiles,TravDistMiH_Pctiles)


View(TravDistMi_Pctiles_All)
TravDistMi_Pctiles_All

Investigation of TravelDistance_Mi_New.

Why are there still some small or large TravelDistance_Mi_NewHvrs values.


# View(filter(AllDays_NewTravelDist,
#             !is.na(TravelDistance_Mi_NewHvrs)
#            ) %>% 
#        select(-TD_Mi_q2,
#               -TD_Mi_q98,
#               -TD_Mi_SS_q5,
#               -TD_Mi_SS_q95,
#               -TD_Mi_SSHG_q5,
#               -TD_Mi_SSHG_q95,
#               -TD_Mi_Mean,
#               -TD_Mi_Mean_F,
#               -TD_Mi_SS_Mean,
#               -TD_Mi_SS_Mean_F,
#               -TD_Mi_SSHG_Mean,
#               -TD_Mi_SSHG_Mean_F,
#               -TD_Mi_Med,
#               -TD_Mi_Med_F,
#               -TD_Mi_SS_Med,
#               -TD_Mi_SS_Med_F,
#               -TD_Mi_SSHG_Med,
#               -TD_Mi_SSHG_Med_F,
#               -TD_Mi_Cnt,
#               -TD_Mi_Cnt_F,
#               -TD_Mi_SS_Cnt,
#               -TD_Mi_SS_Cnt_F,
#               -TD_Mi_SSHG_Cnt,
#               -TD_Mi_SSHG_Cnt_F,
#               -TT_Sec_q2,
#               -TT_Sec_q98,
#               -TT_Sec_SS_q5,
#               -TT_Sec_SS_q95,
#               -TT_Sec_SSHG_q5,
#               -TT_Sec_SSHG_q95,
#               -TT_Sec_Mean,
#               -TT_Sec_Mean_F,
#               -TT_Sec_SS_Mean,
#               -TT_Sec_SS_Mean_F,
#               -TT_Sec_SSHG_Mean,
#               -TT_Sec_SSHG_Mean_F,
#               -TT_Sec_Med,
#               -TT_Sec_Med_F,
#               -TT_Sec_SS_Med,
#               -TT_Sec_SS_Med_F,
#               -TT_Sec_SSHG_Med,
#               -TT_Sec_SSHG_Med_F,
#               -TT_Sec_Cnt,
#               -TT_Sec_Cnt_F,
#               -TT_Sec_SS_Cnt,
#               -TT_Sec_SS_Cnt_F,
#               -TT_Sec_SSHG_Cnt,
#               -TT_Sec_SSHG_Cnt_F,
#               -TT_Hr_q2,
#               -TT_Hr_q98,
#               -TT_Hr_SS_q5,
#               -TT_Hr_SS_q95,
#               -TT_Hr_SSHG_q5,
#               -TT_Hr_SSHG_q95,
#               -TT_Hr_Mean,
#               -TT_Hr_Mean_F,
#               -TT_Hr_SS_Mean,
#               -TT_Hr_SS_Mean_F,
#               -TT_Hr_SSHG_Mean,
#               -TT_Hr_SSHG_Mean_F,
#               -TT_Hr_Med,
#               -TT_Hr_Med_F,
#               -TT_Hr_SS_Med,
#               -TT_Hr_SS_Med_F,
#               -TT_Hr_SSHG_Med,
#               -TT_Hr_SSHG_Med_F,
#               -TT_Hr_Cnt,
#               -TT_Hr_Cnt_F,
#               -TT_Hr_SS_Cnt,
#               -TT_Hr_SS_Cnt_F,
#               -TT_Hr_SSHG_Cnt,
#               -TT_Hr_SSHG_Cnt_F
#              ) %>% 
#        arrange(TravelDistance_Mi_NewHvrs) %>% 
#        head(500)
#     )

View(filter(AllDays_NewTravelDist,
            !is.na(TravelDistance_Mi_NewHvrs)
           ) %>% 
       select(-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
             ) %>% 
       arrange(TravelDistance_Mi_NewHvrs) %>%
       head(500)
    )

# examples of the smallest TravelDistance_Mi_NewHvrs values.
View(filter(AllDays_NewTravelDist,
            (RowNum_OG >= 1424440 & RowNum_OG <= 1424460) | # 1424450  --  direction change
                (RowNum_OG >= 763292 & RowNum_OG <= 763312) | # 763302  --  direction change
                (RowNum_OG >= 1679093 & RowNum_OG <= 1679113) | # 1679103  --  direction change
                (RowNum_OG >= 2860918 & RowNum_OG <= 2860938) # 2860928  --  looks correct
           ) %>% 
       select(-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
             )
    )


View(filter(AllDays_NewTravelDist,
            !is.na(TravelDistance_Mi_NewHvrs)
           ) %>% 
       select(-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
             ) %>% 
       arrange(desc(TravelDistance_Mi_NewHvrs)
              ) %>%
       head(500)
    )

# examples of the largest TravelDistance_Mi_NewHvrs values.
View(filter(AllDays_NewTravelDist,
            (RowNum_OG >= 1092000 & RowNum_OG <= 1092050) | # 1092030  --  direction change
                (RowNum_OG >= 1609460 & RowNum_OG <= 1609480) | # 1609470  -- direction change 
                (RowNum_OG >= 508904 & RowNum_OG <= 508924) | # 508914  --  direction change & original StopID was bad
                (RowNum_OG >= 2476345 & RowNum_OG <= 2476365) # 2476355  --  direction change
           ) %>% 
       select(-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
             )
    )

Investigation of TravelTime_Hr.

View(TravDistMi_Pctiles): 98% of TravelTime_Hr are between 7 seconds and 464 seconds (~8 minutes).


TravTimeHr_Ntile <- select(AllDays_NewTravelDist,
                           TravelTime_Hr
                          ) %>% 
  mutate(# Pctile = ntile(AllDays_NewTravelDist$TravelTime_Hr, 100),
         # MinR = min_rank(AllDays_NewTravelDist$TravelTime_Hr),
         PctR = percent_rank(AllDays_NewTravelDist$TravelTime_Hr),
         PctR_Round = round(PctR, 2)
        ) 

# str(TravTimeHr_Ntile)

TravTimeHr_Ntile_Rows <- nrow(TravTimeHr_Ntile)

# View(tail(TravTimeHr_Ntile, 500))


TravTimeHr_Pctiles <- group_by(TravTimeHr_Ntile,
                               PctR_Round
                              ) %>% 
  summarise(
    MinTravTimeHrAtPctile = min(TravelTime_Hr),
    CntsAtPctile = n(),
    PctsAtPctile = CntsAtPctile / TravTimeHr_Ntile_Rows
  ) %>% 
  mutate(CumSumPAtP = cumsum(PctsAtPctile),
         MinTravTimeSecAtPctile = MinTravTimeHrAtPctile * 3600
        )

rm(TravTimeHr_Ntile_Rows)
rm(TravTimeHr_Ntile)
View(TravTimeHr_Pctiles)
TravTimeHr_Pctiles

Investigation of TravelTime_Hr.

Histogram of TravelTime_Sec.


TravTime_Sec_HistDen <- ggplot(filter(select(AllDays_NewTravelDist,
                                             TravelTime_Sec
                                            ),
                                      !is.na(TravelTime_Sec)
                                     ),
                               aes(x = TravelTime_Sec,
                                   y = ..density..
                                  )
                          ) +
  geom_histogram(binwidth = 5, fill = "lightblue", colour = "grey60", size = 0.2) +
  geom_line(stat = "density", colour = "red") +
  # stat_bin(binwidth = 5,
  #          geom = "text",
  #          size = 2.5,
  #          vjust = 1.5,
  #          aes(label = format(..count.., big.mark = ",")
  #             ),
  #         ) +
  coord_cartesian(xlim = c(0, 180), ylim = c(0, 0.02)
                 ) +
  #  theme(legend.position="none") +
  labs(title = "Variation in Travel Time",
       x = "Travel Time (sec)",
       y = "Density"
      )

TravTime_Sec_HistDen

Investigation of TravelTime_Sec.

TravelTime_Sec values are NA.


summary(AllDays_NewTravelDist$TravelTime_Sec)


View(select(AllDays_NewTravelDist,
            -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
           ) %>% 
       filter(is.na(TravelTime_Sec) &
                BusDay_EventNum != 1  # TravelTime purposefully not calculated here
             )
    )

# examples of TravelTime_Sec values that are NA. These are NA because the Event_Time & Departure_Time readings are not accurate (i.e., the previous Departure_Time is BEFORE or EQUAL TO the current Event_Time).
View(filter(AllDays_NewTravelDist,
            (RowNum_OG >= 90809 & RowNum_OG <= 90829) | # 90819
                (RowNum_OG >= 90881 & RowNum_OG <= 90901) | # 90891
                (RowNum_OG >= 2597066 & RowNum_OG <= 2597086) | # 2597076
                (RowNum_OG >= 2613305 & RowNum_OG <= 2613325) # 2613315
           ) %>% 
       select(-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt"))
    )

Investigation of TravelTime_Sec.

TravelTime_Sec values are extremely small.


View(select(AllDays_NewTravelDist,
            -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
           ) %>% 
       filter(!is.na(TravelTime_Sec)
             ) %>% 
       arrange(TravelTime_Sec,
               desc(SpeedAvg_Mph_NewHvrs)
              ) %>%
       head(500)
    )

# examples where TravelTime_Sec is small (1 sec) and SpeedAvg_Mph_NewHvrs is large.
View(select(AllDays_NewTravelDist,
            -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
           ) %>% 
       filter((RowNum_OG >= 2217353 & RowNum_OG <= 2217373) | # 2217363
                (RowNum_OG >= 3090321 & RowNum_OG <= 3090341) | # 3090331
                (RowNum_OG >= 80764 & RowNum_OG <= 80784) | # 80774
                (RowNum_OG >= 33840 & RowNum_OG <= 33860) # 33850
           )
    )

Investigation of TravelTime_Sec.

TravelTime_Sec values are extremely large.


View(select(AllDays_NewTravelDist,
            -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
           ) %>% 
       filter(!is.na(TravelTime_Sec)
             ) %>% 
       arrange(desc(TravelTime_Sec),
               SpeedAvg_Mph_NewHvrs
              ) %>%
       head(500)
    )

# examples where TravelTime_Sec is large and SpeedAvg_Mph_NewHvrs is small.
View(select(AllDays_NewTravelDist,
            -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
           ) %>% 
       filter((RowNum_OG >= 1007703 & RowNum_OG <= 1007723) | # 1007713
                (RowNum_OG >= 2373564 & RowNum_OG <= 2373584) | # 2373574
                (RowNum_OG >= 864379 & RowNum_OG <= 864399) | # 864389
                (RowNum_OG >= 2570060 & RowNum_OG <= 2570080) # 2570070
           )
    )

Investigation of TravelTime_Sec.

Are large TravelTime_Sec values related to RouteChanges? Looks likely. When the Bus involves a Route “change”, there is almost twice as likely to be a case of an outlier TravelTime_Sec value (on the high side).


TTLargeRteChng <- select(AllDays_NewTravelDist,
                         -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
                        ) %>% 
  mutate(TT_Out = factor(ifelse(TravelTime_Sec > 464,  # this is the 99th percentile
                                "Outlier",
                                "Normal"
                               )
                        )
        )

# str(TTLargeRteChng)


TTLargeRteChng_Cnts <- group_by(TTLargeRteChng,
                                RteChange2,
                                TT_Out
                               ) %>% 
  summarise(Cnts = n()
           )

TTLargeRteChng_Spread <- as.data.frame(spread(TTLargeRteChng_Cnts,
                                              TT_Out,
                                              Cnts
                                             )
                                      ) %>%
  select(-RteChange2)

row.names(TTLargeRteChng_Spread) <- c("Change", "Same")
# str(TTLargeRteChng_Spread)


# When the Bus involves a Route "change", there is almost twice as likely to be a case of an outlier TravelTime_Sec value.
TTLargeRteChng_Spread
prop.table(as.table(as.matrix(TTLargeRteChng_Spread)
                   ),
           1
          )

prop.table(as.table(as.matrix(TTLargeRteChng_Spread)
                   ),
           2
          )

# rm(TTLargeRteChng, TTLargeRteChng_Spread)
         

Investigation of TravelTime_Sec.

Are large TravelTime_Sec values related to RouteChanges? Looks likely.


View(filter(TTLargeRteChng,
            !is.na(TravelTime_Sec) &
              RteChange2 == "Same"
           ) %>% 
       arrange(desc(TravelTime_Sec),
               SpeedAvg_Mph_NewHvrs
              ) %>%
       head(500)
    )


# examples where TravelTime_Sec is large and SpeedAvg_Mph_NewHvrs is small.
View(filter(TTLargeRteChng,
            (RowNum_OG >= 2250290 & RowNum_OG <= 2250310) | # 2250300
              (RowNum_OG >= 867717 & RowNum_OG <= 867737) | # 867727
              (RowNum_OG >= 864379 & RowNum_OG <= 864399) | # 864389
              (RowNum_OG >= 808395 & RowNum_OG <= 808415) # 808405
           )
    )

Investigation of TravelTime_Sec.

If TravelTime_Sec is below the 5th percentile for that StartStop_ID, or if TravelTime_Sec is above the 95th percentile for that StartStop_ID, consider this an outlier. In this case, replace the value with the mean for that StartStop_ID and HourGroup (TT_Sec_SSHG_Mean_F), or if there are not enough values at the HourGroup level, replace it with the mean for that StartStop_ID.


rm(TTLargeRteChng, TTLargeRteChng_Cnts, TTLargeRteChng_Spread)


NewTravTime <- mutate(AllDays_NewTravelDist,
                      TT_Sec_New = ifelse(!is.na(TravelTime_Sec) &
                                            (TravelTime_Sec < TT_Sec_SSHG_q5 |
                                               TravelTime_Sec > TT_Sec_SSHG_q95
                                            ) &
                                            TT_Sec_SSHG_Cnt_F >= 20,
                                          TT_Sec_SSHG_Mean_F,
                                   ifelse(!is.na(TravelTime_Sec) &
                                            (TravelTime_Sec < TT_Sec_SSHG_q5 |
                                               TravelTime_Sec > TT_Sec_SSHG_q95
                                            ) &
                                            TT_Sec_SSHG_Cnt_F < 20 &
                                            TT_Sec_SS_Cnt_F >= 20,
                                          TT_Sec_SS_Mean_F,
                                   ifelse(!is.na(TravelTime_Sec) &
                                            (TravelTime_Sec < TT_Sec_SSHG_q5 |
                                               TravelTime_Sec > TT_Sec_SSHG_q95
                                            ) &
                                            TT_Sec_SS_Cnt_F < 20 &
                                            TT_Sec_SS_Cnt >= 20,
                                          TT_Sec_SS_Mean,
                                   ifelse(!is.na(TravelTime_Sec) &
                                            (TravelTime_Sec < TT_Sec_SSHG_q5 |
                                               TravelTime_Sec > TT_Sec_SSHG_q95
                                            ) &
                                            TT_Sec_SS_Cnt_F < 20 &
                                            TT_Sec_SS_Cnt < 20 &
                                            RteChange2 == "Change",
                                          NA,
                                          TravelTime_Sec
                                         )))),
                      
                      TT_Sec_New_Label = 
           factor(ifelse(!is.na(TravelTime_Sec) &
                           (TravelTime_Sec < TT_Sec_SSHG_q5 |
                              TravelTime_Sec > TT_Sec_SSHG_q95
                           ) &
                           TT_Sec_SSHG_Cnt_F >= 20,
                         "TT_Sec_SSHG_Mean_F",
                  ifelse(!is.na(TravelTime_Sec) &
                           (TravelTime_Sec < TT_Sec_SSHG_q5 |
                              TravelTime_Sec > TT_Sec_SSHG_q95
                           ) &
                           TT_Sec_SSHG_Cnt_F < 20 &
                           TT_Sec_SS_Cnt_F >= 20,
                         "TT_Sec_SS_Mean_F",
                  ifelse(!is.na(TravelTime_Sec) &
                           (TravelTime_Sec < TT_Sec_SSHG_q5 |
                              TravelTime_Sec > TT_Sec_SSHG_q95
                            ) &
                           TT_Sec_SS_Cnt_F < 20 &
                           TT_Sec_SS_Cnt >= 20,
                         "TT_Sec_SS_Mean",
                  ifelse(!is.na(TravelTime_Sec) &
                           (TravelTime_Sec < TT_Sec_SSHG_q5 |
                              TravelTime_Sec > TT_Sec_SSHG_q95
                           ) &
                           TT_Sec_SS_Cnt_F < 20 &
                           TT_Sec_SS_Cnt < 20 &
                           RteChange2 == "Change",
                         NA,
                         "TravelTime_Sec"
                        ))))
                 ),
                  
                  TT_Hr_New = TT_Sec_New / (60 * 60)
           )


dim(AllDays_NewTravelDist)
dim(NewTravTime)
rm(AllDays_NewTravelDist)

summary(select(NewTravTime,
           -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
          )
   )

str(select(NewTravTime,
           TravelTime_Sec,
           TT_Sec_New,
           TT_Sec_New_Label,
           TT_Hr_New
          )
   )


summary(select(NewTravTime,
               TravelTime_Sec,
               TT_Sec_New,
               TT_Sec_New_Label,
               TT_Hr_New
              )
       )

Test investigation of just the X2 Route. Box plots for time between bus arrivals (by HourGroup).


View(head(select(NewTravTime,
                 -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
                )
         )
    )

X2 <- select(NewTravTime,
             -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
            ) %>% 
  filter(Route == "X2")

str(X2)

View(head(arrange(X2,
                  Bus_ID,
                  Event_Time
                 ),
          500
         )
    )

X2_ByStop <- group_by(X2,
                      StopID_Clean
                     ) %>% 
  arrange(StopID_Clean,
          Event_Time) %>% 
  mutate(Event_Time_L1 = lag(Event_Time),
         TimeToEvent_Sec = as.numeric(Event_Time - Event_Time_L1),
         TimeToEvent_Min = TimeToEvent_Sec / 60
        )

View(head(X2_ByStop, 500))


# Count_Values is needed to display the medians on the box plots
Count_Values <- ddply(as.data.frame(X2_ByStop),
                      .(Event_Time_HrGroup),
                      summarise,
                      Value_Counts = median(TimeToEvent_Min, na.rm = TRUE)
                     )

TimeBtwEvents_X2_BoxPlot <- ggplot(select(as.data.frame(X2_ByStop),
                                          TimeToEvent_Min,
                                          Event_Time_HrGroup
                                         ),
                                   aes(factor(Event_Time_HrGroup),
                                       TimeToEvent_Min,
                                       fill = factor(Event_Time_HrGroup)
                                      )
                                  ) + 
  geom_boxplot(outlier.colour="red", notch=TRUE, na.rm = TRUE) +
  geom_text(data = Count_Values,
            aes(y = Value_Counts,
                label = format(round(Value_Counts, digits = 1),
                               nsmall = 1
                              )
               ),
            size = 3,
            vjust = -0.5
           ) +
  theme(legend.position="none", axis.text.x = element_text(angle=45)) +
  coord_cartesian(# xlim = c(0, 180),
                  ylim = c(0, 120)
                 ) +
  labs(title = "How Often an X2 Arrives at a Given Stop",
       x = "Hour Group",
       y = "Time Between Busses (min)"
      )

TimeBtwEvents_X2_BoxPlot

Test investigation of just the X2 Route. Violin plots for time between bus arrivals (by Hour Group).


TimeBtwEvents_X2_ViolinPlot <- ggplot(select(as.data.frame(X2_ByStop),
                                             TimeToEvent_Min,
                                             Event_Time_HrGroup
                                             ),
                                      aes(factor(Event_Time_HrGroup),
                                          TimeToEvent_Min,
                                          fill = factor(Event_Time_HrGroup)
                                         )
                                     ) + 
  geom_violin(draw_quantiles = c(0.25, 0.5, 0.75),
              trim = TRUE,
              scale = "count",
              na.rm = TRUE,
              show.legend = NA,
              inherit.aes = TRUE
             ) +
  geom_text(data = Count_Values,
            aes(y = Value_Counts,
                label = format(round(Value_Counts, digits = 1),
                               nsmall = 1
                              )
               ),
            size = 2.5,
            vjust = -0.5
           ) +
  theme(legend.position="none", axis.text.x = element_text(angle=45)) +
  coord_cartesian(# xlim = c(0, 180),
                  ylim = c(0, 80)
                 ) +
  labs(title = "How Often an X2 Arrives at a Given Stop",
       x = "Hour Group",
       y = "Time Between Busses (min)"
      )

TimeBtwEvents_X2_ViolinPlot

Test investigation of just the X2 Route. Box plots for time between bus arrivals (by Zip Code).


# Count_Values is needed to display the medians on the box plots
Count_Values_z <- ddply(as.data.frame(X2_ByStop),
                        .(Stop_Zip),
                        summarise,
                        Value_Counts = median(TimeToEvent_Min, na.rm = TRUE)
                       )

TimeBtwEvents_X2_BoxPlot_z <- ggplot(select(as.data.frame(X2_ByStop),
                                            TimeToEvent_Min,
                                            Stop_Zip
                                           ),
                                     aes(factor(Stop_Zip),
                                         TimeToEvent_Min,
                                         fill = factor(Stop_Zip)
                                        )
                                    ) + 
  geom_boxplot(outlier.colour="red", notch=TRUE, na.rm = TRUE) +
  geom_text(data = Count_Values_z,
            aes(y = Value_Counts,
                label = format(round(Value_Counts, digits = 1),
                               nsmall = 1
                              )
               ),
            size = 3,
            vjust = -0.5
           ) +
  theme(legend.position="none", axis.text.x = element_text(angle=45)) +
  coord_cartesian(# xlim = c(0, 180),
                  ylim = c(0, 100)
                 ) +
  labs(title = "How Often an X2 Arrives at a Given Stop",
       x = "Zip Code of Destination",
       y = "Time Between Busses (min)"
      )

TimeBtwEvents_X2_BoxPlot_z

Test investigation of just the X2 Route. Violin plots for time between bus arrivals (by Zip Code).


TimeBtwEvents_X2_ViolinPlot_z <- ggplot(select(as.data.frame(X2_ByStop),
                                               TimeToEvent_Min,
                                               Stop_Zip
                                               ),
                                        aes(factor(Stop_Zip),
                                            TimeToEvent_Min,
                                            fill = factor(Stop_Zip)
                                           )
                                       ) + 
  geom_violin(draw_quantiles = c(0.25, 0.5, 0.75),
              trim = TRUE,
              scale = "count",
              na.rm = TRUE,
              show.legend = NA,
              inherit.aes = TRUE
             ) +
  geom_text(data = Count_Values_z,
            aes(y = Value_Counts,
                label = format(round(Value_Counts, digits = 1),
                               nsmall = 1
                              )
               ),
            size = 2.5,
            vjust = -0.5
           ) +
  theme(legend.position="none", axis.text.x = element_text(angle=45)) +
  coord_cartesian(# xlim = c(0, 180),
                  ylim = c(0, 60)
                 ) +
  labs(title = "How Often an X2 Arrives at a Given Stop",
       x = "Zip Code of Destination",
       y = "Time Between Busses (min)"
      )

TimeBtwEvents_X2_ViolinPlot_z

Waiting time analyses.

Munging and sampling data to go from time beteen buses to “average” waiting time.

First, get the max and min times of bus stops (each day, and for each route).


rm(X2, X2_ByStop, X2_Long, X2_Pct)


RouteMinMax <- group_by(NewTravTime,
                        Route,
                        Event_Time_Date
                       ) %>% 
  summarise(MinTime = min(Event_Time),
            MaxTime = max(Event_Time)
           )

str(RouteMinMax)
View(RouteMinMax)

Waiting time analyses.

Munging and sampling data to go from time beteen buses to “average” waiting time.

(Pulls here are done by day, as the data are too large to do at once.)


# View(head(NewTravTime, 500))

# For each record, create a random datetime between the first and last stop for that bus route (on that day).
for(i in 3:7){

set.seed(123456789)
Samp <- select(NewTravTime,
               RowNum_OG,
               Route,
               # RouteGroup,
               Event_Time_Date,
               StopID_Clean,
               starts_with("Event")
              ) %>% 
  filter(Event_Time_Date == i) %>%  # needed to do this each day (3-7) because the complete file was too large to do at once
  left_join(RouteMinMax,
            by = c("Route" = "Route",
                   "Event_Time_Date" = "Event_Time_Date"
                  )
           ) %>% 
  mutate(SampTime = as_datetime(runif(nrow(.), #200000,
                                      min = MinTime,
                                      max = MaxTime
                                     ),
                                tz = "America/New_York"
                               )
        ) %>% 
  arrange(Route,
          StopID_Clean,
          Event_Time
         ) 

# str(Samp)
# View(head(Samp, 500))
# 
# View(
# group_by(Samp,
#          RowNum_OG
#         ) %>%
#   summarise(Cnt_Num = n(),
#             Cnt_Pct = 100 * Cnt_Num / nrow(Samp)
#            ) %>%
#   arrange(desc(Cnt_Num))
# )


# For each Route and StopID combination, get all the Event_Time values that are after the SampTime value.
# estimating approx 2hrs of runtime for all 2.8m records
Testing_A <- sqldf("   Select               t1.*
                                            ,t2.Event_Time             as NextBus
                        From                 Samp                      as t1
                             Inner Join      Samp                      as t2
                                On              t1.Route = t2.Route
                                And             t1.StopID_Clean = t2.StopID_Clean
                                And             t2.Event_Time > t1.SampTime
                        Order By             t1.Route
                                            ,t1.StopID_Clean
                                            ,t1.Event_Time
                                            ,t2.Event_Time
                  "
                 ) %>% 
  mutate(NB = as_datetime(NextBus,
                          tz = "America/New_York"
                         )
        )

# str(Testing_A)
# View(head(Testing_A, 500))
# View(head(Samp, 500))


# Filter the dataframe to only include the bus arrival at StopID that is the next to come after the SampTime.
# estimating approx 20min of runtime for all 2.8m records
Testing <- select(Testing_A,
                  -NextBus
                 ) %>% 
  group_by(RowNum_OG) %>% 
  filter(NB == min(NB)
        ) %>% 
  arrange(Route,
          StopID_Clean,
          Event_Time
         ) %>% 
  mutate(WaitTime_Min = as.numeric(NB - SampTime),
         WaitTime_Sec = WaitTime_Min * 60,
         WaitTime_Sec2 = NB - SampTime,
         WaitTime_Min2 = WaitTime_Sec2 / 60
        ) %>% 
  as.data.frame()

assign(paste0("Testing_", i),
       Testing
      )

rm(Samp,Testing_A, Testing)
str(get(paste0("Testing_", i)))
View(get(paste0("Testing_", i)))
}


# Bind all the individual dataframes together.
WaitData_DayPull <- bind_rows(Testing_3,
                              Testing_4,
                              Testing_5,
                              Testing_6,
                              Testing_7
                             ) %>% 
  mutate(WaitTime_Sec3 = NB - SampTime,
         WaitTime_Min3 = WaitTime_Sec3 / 60
        ) %>% 
  arrange(Route,
          StopID_Clean,
          Event_Time
         )

rm(Testing_3, Testing_4, Testing_5, Testing_6, Testing_7)
str(WaitData_DayPull)
View(head(WaitData_DayPull, 500))
View(tail(WaitData_DayPull, 500))

Waiting time analyses.

Munging and sampling data to go from time beteen buses to “average” waiting time.

Basic investigation of any missing rows from data pulled by day.


DistinctRowNum_OG <- distinct(select(WaitData_DayPull,
                                     RowNum_OG
                                    )
                             )

str(DistinctRowNum_OG)

# View(
# anti_join(Samp,
#           DistinctRowNum_OG,
#           by = c("RowNum_OG" = "RowNum_OG")
#          )
# )


# The samp time is AFTER the last bus passed that StopID_Clean
# View(filter(Samp,
#             Event_Time > "2016-10-07 19:48:41" &
#               Route == "X2" &
#               StopID_Clean == 1003774
#            )
#     )

# Next Bus (NB) can be on the next morning
# View(filter(Testing7,
#             SampTime > "2016-10-06 23:58:00" &
#               SampTime < "2016-10-06 23:59:59")
#     )

Waiting time analyses.

Munging and sampling data to go from time beteen buses to “average” waiting time.

(Pulls here are done by groupings of bus routes, as the data are too large to do at once.)

First, we need to find the most common bus routes.


rm(DistinctRowNum_OG)


# View(head(NewTravTime, 500))

set.seed(123456789)
BusGroups <- group_by(NewTravTime,
                      Route
                     ) %>% 
  summarise(Cnt_Num = n(),
            Cnt_Pct = Cnt_Num / nrow(NewTravTime)
           ) %>% 
  arrange(desc(Cnt_Num)
         ) %>% 
  mutate(RowNum = row_number(),
         RandNum = runif(n = 268),
         RouteGroup = ifelse(RandNum <= 0.2,
                             1,
                      ifelse(RandNum <= 0.4,
                             2,
                      ifelse(RandNum <= 0.6,
                             3,
                      ifelse(RandNum <= 0.8,
                             4,
                             5
                            ))))
        )

str(BusGroups)
View(BusGroups)
summary(BusGroups)

Waiting time analyses.

Munging and sampling data to go from time beteen buses to “average” waiting time.

(Pulls here are done by groupings of bus routes, as the data are too large to do at once.)


# View(head(NewTravTime, 500))

# For each record, create a random datetime between the first and last stop for that bus route (on that day).
for(i in 1:5){
  
set.seed(123456789)
Samp <- left_join(NewTravTime,
                  BusGroups,
                  by = c("Route" = "Route")
                  ) %>% 
  select(RowNum_OG,
         Route,
         RouteGroup,
         Event_Time_Date,
         StopID_Clean,
         starts_with("Event")
        ) %>% 
  filter(RouteGroup == i) %>%  # needed to do this each RouteGroup (1-5) because the complete file was too large to do at once
  left_join(RouteMinMax,
            by = c("Route" = "Route",
                   "Event_Time_Date" = "Event_Time_Date"
                  )
           ) %>% 
  mutate(SampTime = as_datetime(runif(nrow(.), #200000,
                                      min = MinTime,
                                      max = MaxTime
                                     ),
                                tz = "America/New_York"
                               )
        ) %>% 
  arrange(Route,
          StopID_Clean,
          Event_Time
         ) 

# str(Samp)
# View(head(Samp, 500))
# 
# View(
# group_by(Samp,
#          RowNum_OG
#         ) %>%
#   summarise(Cnt_Num = n(),
#             Cnt_Pct = 100 * Cnt_Num / nrow(Samp)
#            ) %>%
#   arrange(desc(Cnt_Num))
# )


# For each Route and StopID combination, get all the Event_Time values that are after the SampTime value.
# estimating approx 2hrs of runtime for all 2.8m records
Testing_A <- sqldf("   Select               t1.*
                                            ,t2.Event_Time             as NextBus
                        From                 Samp                      as t1
                             Inner Join      Samp                      as t2
                                On              t1.Route = t2.Route
                                And             t1.StopID_Clean = t2.StopID_Clean
                                And             t2.Event_Time > t1.SampTime
                        Order By             t1.Route
                                            ,t1.StopID_Clean
                                            ,t1.Event_Time
                                            ,t2.Event_Time
                  "
                 ) %>% 
  mutate(NB = as_datetime(NextBus,
                          tz = "America/New_York"
                         )
        )

# str(Testing_A)
# View(head(Testing_A, 500))
# View(head(Samp, 500))


# Filter the dataframe to only include the bus arrival at StopID that is the next to come after the SampTime.
# estimating approx 20min of runtime for all 2.8m records
Testing <- select(Testing_A,
                  -NextBus
                 ) %>% 
  group_by(RowNum_OG) %>% 
  filter(NB == min(NB)
        ) %>% 
  arrange(Route,
          StopID_Clean,
          Event_Time
         ) %>% 
  mutate(WaitTime_Min = as.numeric(NB - SampTime),
         WaitTime_Sec = WaitTime_Min * 60
        ) %>% 
  as.data.frame()

assign(paste0("Testing", i),
       Testing
      )

rm(Samp,Testing_A, Testing)
str(get(paste0("Testing", i)))
View(get(paste0("Testing", i)))
}


# Bind all the individual dataframes together.
WaitData_RoutePull <- bind_rows(Testing1,
                                Testing2,
                                Testing3,
                                Testing4,
                                Testing5
                             ) %>% 
  mutate(WaitTime_Sec2 = NB - SampTime,
         WaitTime_Min2 = WaitTime_Sec2 / 60
        ) %>% 
  arrange(Route,
          StopID_Clean,
          Event_Time
         )

rm(BusGroups, i, Testing3, Testing4, Testing5, Testing6, Testing7)
str(WaitData_RoutePull)
View(head(WaitData_RoutePull, 500))
View(tail(WaitData_RoutePull, 500))

Waiting time analyses.

Munging and sampling data to go from time beteen buses to “average” waiting time.

Compare WaitData pulled by day and pulled by route.


dim(WaitData_RoutePull)
dim(WaitData_DayPull)
nrow(WaitData_RoutePull) - nrow(WaitData_DayPull)

WaitData_Diff <- anti_join(WaitData_RoutePull,
                           WaitData_DayPull,
                           by = c("RowNum_OG" = "RowNum_OG"
                                 )
                          ) %>% 
  select(-WaitTime_Min,
         -WaitTime_Sec
        )

str(WaitData_Diff)
View(head(WaitData_Diff, 500))

View(filter(WaitData_RoutePull,
            Route == "Z8" &
              StopID_Clean == 2005465
            # RowNum_OG = 2902760
            # Event_Time = 2016-10-07 19:51:47
           )
    )

View(group_by(WaitData_Diff,
              Route
             ) %>% 
       summarise(Cnt_Num = n(),
                 Cnt_Pct = Cnt_Num / nrow(WaitData_Diff)
                ) %>% 
       arrange(desc(Cnt_Num)
              )
    )

View(filter(WaitData_Diff,
            Route == "S1"
           )
    )

View(filter(WaitData_RoutePull,
            Route == "S1" &
              StopID_Clean == 1003132
            # RowNum_OG = 1151770
            # Event_Time = 2016-10-07 09:07:12
           )
    )

# Can't tell why the pull by day has less records than the pull by route

Waiting time analyses.

Munging and sampling data to go from time beteen buses to “average” waiting time.

Compare WaitData (pulled by route) and original data (NewTravTime).


dim(NewTravTime)  # 2,809,529 rows
dim(WaitData_RoutePull)  # 2,780,848 rows
nrow(NewTravTime) - nrow(WaitData_RoutePull)  # is 28,681 rows

str(select(NewTravTime,
           -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
          )
   )
str(WaitData_RoutePull)

Compare_NTT_WD <- left_join(NewTravTime,
                            select(WaitData_RoutePull,
                                   RowNum_OG,
                                   # Route,
                                   RouteGroup,
                                   # StopID_Clean,
                                   # Event_Time,
                                   MinTime,
                                   MaxTime,
                                   SampTime,
                                   NB,
                                   WaitTime_Sec2,
                                   WaitTime_Min2
                                  ),
                            by = c("RowNum_OG" = "RowNum_OG")
                           ) %>% 
  select(-matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
        ) %>% 
  arrange(Route,
          StopID_Clean,
          Event_Time
         )

str(Compare_NTT_WD)  # 2,810,109 rows overall  --  29,261 rows with no match
View(head(Compare_NTT_WD, 500))
View(filter(Compare_NTT_WD,
            is.na(MinTime)
           )
    )



# View(anti_join(Samp,
#                distinct(select(WaitData_RoutePull,
#                                RowNum_OG
#                               )
#                        ),
#                by = c("RowNum_OG" = "RowNum_OG")
#               )
#     )

# The SampTime is AFTER the last bus passed that StopID_Clean
# View(filter(Samp,
#               Route == "X2" &
#               StopID_Clean == 1003774
#             # RowNum_OG = 1146723
#             # Event_Time = 2016-10-07 15:32:18
#            )
#     )

Clean up the data a bit.


rm(BusGroups, RouteMinMax, Samp, Testing1, Testing2, Testing3, Testing4, Testing5, Testing_3, Testing_4, Testing_5, Testing_6, Testing_7, WaitData_DayPull, WaitData_Diff)


str(Compare_NTT_WD)
View(head(Compare_NTT_WD, 500))
View(head(mutate(Compare_NTT_WD,
                 WT_Min = as.numeric(WaitTime_Min2)
                )
         )
    )

WaitTime_AsNum <- Compare_NTT_WD %>% 
  mutate(RouteStop_ID = factor(paste(Route, StopID_Clean, sep = "__")
                              )
        )
WaitTime_AsNum$WaitTime_Sec2 <- as.numeric(WaitTime_AsNum$WaitTime_Sec2)
WaitTime_AsNum$WaitTime_Min2 <- as.numeric(WaitTime_AsNum$WaitTime_Min2)

rm(Compare_NTT_WD)
str(WaitTime_AsNum)

General exploration of wait times.


summary(WaitTime_AsNum$WaitTime_Min2)

General exploration of wait times.


WT_Quantiles <- as.data.frame(quantile(WaitTime_AsNum$WaitTime_Min2,
                                       probs = seq(0, 1, 0.01),
                                       na.rm = TRUE
                                      )
                             )

colnames(WT_Quantiles) <- "Value_Min"

WT_Quantiles$Value_Sec = format(round(WT_Quantiles$Value_Min * 60,
                                      digits = 2
                                     ),
                                nsmall = 2
                               )
WT_Quantiles$Value_Hr = format(round(WT_Quantiles$Value_Min / 60,
                                     digits = 2
                                    ),
                                nsmall = 2
                               )
WT_Quantiles$Value_Min = format(round(WT_Quantiles$Value_Min,
                                      digits = 2
                                     ),
                                nsmall = 2
                               )

WT_Quantiles$Quantile <- seq(0, 1, 0.01)

WT_Quantiles <- select(WT_Quantiles,
                       Quantile,
                       Value_Sec,
                       Value_Min,
                       Value_Hr
                      )

str(WT_Quantiles)
View(WT_Quantiles)
WT_Quantiles


View(arrange(WaitTime_AsNum,
             desc(WaitTime_Min2)
            ) %>% 
       head(., 5000)
    )

View(filter(WaitTime_AsNum,
            between(WaitTime_Min2, 60, 200)
           ) %>% 
       arrange(desc(WaitTime_Min2)
              ) 
     # %>% 
     #   head(., 5000)
    )

# Example of extreme wait times
View(filter(WaitTime_AsNum,
            Route == "W13" &  # only 2 bus passes in the entire dataset
              StopID_Clean == 1003728
            # Event_Time = 2016-10-03 08:42:46
           )
    )

# Example of extreme wait times
View(filter(WaitTime_AsNum,
            Route == "S41" &  # only 4 bus passes in the entire dataset
              StopID_Clean == 1001095
            # Event_Time = 2016-10-05 15:41:47
           )
    )

# Example of extreme wait times
View(filter(WaitTime_AsNum,
            Route == "D8" &  # route has VERY limited service after midnight
              StopID_Clean == 1001669
            # Event_Time = 2016-10-06 20:31:16
           )
    )

Looks like there might be an issue in wait times when very few Route-Stop combinations are included in the dataset. Let’s explore these.


RouteStop_Cnts <- group_by(WaitTime_AsNum,
                           RouteStop_ID
                          ) %>% 
  summarise(RouteStop_CntNum = n(),
            RouteStop_CntPct = RouteStop_CntNum / nrow(WaitTime_AsNum)
           ) %>% 
  arrange(RouteStop_CntNum)

View(RouteStop_Cnts)


RouteStop_CntOfCnt <- group_by(RouteStop_Cnts,
                               RouteStop_CntNum
                              ) %>% 
  summarise(RouteStopCnt_CntNum = n(),
            RouteStopCnt_CntPct = RouteStopCnt_CntNum / nrow(RouteStop_Cnts)
           ) %>% 
  mutate(RouteStopCnt_CntPct_CumSum = cumsum(RouteStopCnt_CntPct),
         x = 1 - RouteStopCnt_CntPct_CumSum
        ) %>% 
  arrange(RouteStop_CntNum)
  
 View(RouteStop_CntOfCnt)
 RouteStop_CntOfCnt

Histogram of the counts of Route-StopID combinations.


RouteStop_Cnts_Bar <- ggplot(RouteStop_CntOfCnt,
                             aes(x = RouteStop_CntNum,
                                 # y = ..density..
                                 y = RouteStopCnt_CntNum
                                )
                            ) +
  # geom_histogram(binwidth = 5, fill = "lightblue", colour = "grey60", size = 0.2) +
  geom_col(fill = "lightblue", colour = "grey60", size = 0.2) +
  coord_cartesian(xlim = c(0, 500)
                  # ylim = c(0, 0.02)
                 ) +
  labs(title = "Variation in Routes Passing a Specific Stop",
       x = "Occurrences of Route-StopID Combiantions",
       y = "Counts"
      )

RouteStop_Cnts_Bar

Create a new dataset limiting extremely small counts of Route-StopID combinations.


WaitTime_RteCnts <- left_join(WaitTime_AsNum,
                              RouteStop_Cnts,
                              by = c("RouteStop_ID" = "RouteStop_ID")
                             ) %>% 
  select(-RouteStop_CntPct)

dim(WaitTime_AsNum)
dim(WaitTime_RteCnts)

rm(WaitTime_AsNum)
str(WaitTime_RteCnts)


# Total rows
nrow(WaitTime_RteCnts)

# Rows of rare RouteStops
nrow(filter(WaitTime_RteCnts,
            RouteStop_CntNum <= 60
           )
    ) / nrow(WaitTime_RteCnts)

# Rows of extremely long wait times
nrow(filter(WaitTime_RteCnts,
            WaitTime_Min2 > 180
           )
    ) / nrow(WaitTime_RteCnts)


select(WaitTime_RteCnts,
       WaitTime_Min2
      ) %>% 
  summary()

filter(WaitTime_RteCnts,
       RouteStop_CntNum > 60  # 12 passes per day in a 5-day dataset
      ) %>% 
  select(WaitTime_Min2) %>% 
  summary()

filter(WaitTime_RteCnts,
       WaitTime_Min2 < 180  # probably means that something went wrong
      ) %>% 
  select(WaitTime_Min2) %>% 
  summary()

Compare quantiles in the limited datasets.


a <- as.data.frame(select(WaitTime_RteCnts,
                          WaitTime_Min2
                         ) %>% 
                     quantile(probs = seq(0, 1, 0.01), na.rm = TRUE)
                  )

b <- as.data.frame(filter(WaitTime_RteCnts,
                          RouteStop_CntNum > 60
                         ) %>% 
                     select(WaitTime_Min2) %>% 
                     quantile(probs = seq(0, 1, 0.01), na.rm = TRUE)
                  )

c <- as.data.frame(filter(WaitTime_RteCnts,
                          WaitTime_Min2 < 180
                         ) %>% 
                     select(WaitTime_Min2) %>% 
                     quantile(probs = seq(0, 1, 0.01), na.rm = TRUE)
                  )

WT_Filter_Quantiles <- bind_cols(a, b, c) %>% 
  mutate(Quantile = seq(0, 1, 0.01)
        )

colnames(WT_Filter_Quantiles) <- c("All", "RteStpAbv60", "WTBlw180", "Quantile")
rm(a, b, c)
View(WT_Filter_Quantiles)
WT_Filter_Quantiles

Histogram of all wait times.


WaitTime_AllBus_HistDen <- ggplot(filter(select(WaitTime_RteCnts,
                                                WaitTime_Min2
                                               ),
                                         !is.na(WaitTime_Min2)
                                        ),
                                  aes(x = WaitTime_Min2,
                                      y = ..density..
                                     )
                                ) +
  geom_histogram(binwidth = 5, fill = "lightblue", colour = "grey60", size = 0.2) +
  geom_line(stat = "density", colour = "red") +
  scale_x_continuous(breaks = seq(0, 300, 30)
                    ) +
  coord_cartesian(xlim = c(0, 300),
                  ylim = c(0, 0.035)
                 ) +
  labs(title = "Variation in Wait Time",
       x = "Wait Time (min)",
       y = "Density"
      )

WaitTime_AllBus_HistDen

Box plots for WaitTime (all busses, by Zip Code).


# Count_Values is needed to display the medians on the box plots
BusRoute <- select(WaitTime_RteCnts,
                   Route,
                   WaitTime_Min2,
                   Stop_Zip
                  ) %>% 
  filter(Route == "X2")

CountValues_AllBus_Zip <- ddply(BusRoute,
                                .(Stop_Zip),
                                summarise,
                                Value_Counts = median(WaitTime_Min2, na.rm = TRUE)
                               )

WaitTime_AllBus_Zip_Box <- ggplot(BusRoute,
                                  aes(factor(Stop_Zip),
                                      WaitTime_Min2,
                                      fill = factor(Stop_Zip)
                                     )
                                 ) + 
  geom_boxplot(outlier.colour="red", notch=TRUE, na.rm = TRUE) +
  geom_text(data = CountValues_AllBus_Zip,
            aes(y = Value_Counts,
                label = format(round(Value_Counts, digits = 1),
                               nsmall = 1
                              )
               ),
            size = 3,
            vjust = -0.5
           ) +
  theme(legend.position="none", axis.text.x = element_text(angle=45)) +
  coord_cartesian(# xlim = c(0, 180),
                  ylim = c(0, 45)
                 ) +
  labs(title = "Waiting Time at a Given Stop (for the X2)",
       x = "Zip Code of Destination",
       y = "Waiting Time (min)"
      )

WaitTime_AllBus_Zip_Box

Test investigation of just the X2 Route. Violin plots for time between bus arrivals (by Zip Code).


WaitTime_AllBus_Zip_Violin <- ggplot(BusRoute,
                                     aes(factor(Stop_Zip),
                                         WaitTime_Min2,
                                         fill = factor(Stop_Zip)
                                        )
                                    ) + 
  geom_violin(draw_quantiles = c(0.25, 0.5, 0.75),
              trim = TRUE,
              scale = "count",
              na.rm = TRUE,
              show.legend = NA,
              inherit.aes = TRUE
             ) +
  geom_text(data = CountValues_AllBus_Zip,
            aes(y = Value_Counts,
                label = format(round(Value_Counts, digits = 1),
                               nsmall = 1
                              )
               ),
            size = 3.5,
            vjust = -0.5
           ) +
  theme(legend.position="none", axis.text.x = element_text(angle=45)) +
  coord_cartesian(# xlim = c(0, 180),
                  ylim = c(0, 45)
                 ) +
  labs(title = "Waiting Time at a Given Stop (for the X2)",
       x = "Zip Code of Destination",
       y = "Waiting Time (min)"
      )

TimeBtwEvents_X2_ViolinPlot_z

Box plots for WaitTime (Zip Code, by HourGroupZip).


# Count_Values is needed to display the medians on the box plots
Zip <- select(WaitTime_RteCnts,
              Route,
              WaitTime_Min2,
              Stop_Zip,
              Event_Time_HrGroup
             ) %>% 
  filter(Stop_Zip == 20002)

CountValues_AllBus_HG <- ddply(Zip,
                               .(Event_Time_HrGroup),
                               summarise,
                               Value_Counts = median(WaitTime_Min2,
                                                     na.rm = TRUE
                                                    )
                               )

WaitTime_AllBus_HG_Box <- ggplot(Zip,
                                 aes(factor(Event_Time_HrGroup),
                                     WaitTime_Min2,
                                     fill = factor(Event_Time_HrGroup)
                                    )
                                ) + 
  geom_boxplot(outlier.colour="red", notch=TRUE, na.rm = TRUE) +
  geom_text(data = CountValues_AllBus_HG,
            aes(y = Value_Counts,
                label = format(round(Value_Counts, digits = 1),
                               nsmall = 1
                              )
               ),
            size = 2.5,
            vjust = -0.5
           ) +
  theme(legend.position="none", axis.text.x = element_text(angle=45)) +
  coord_cartesian(# xlim = c(0, 180),
                  ylim = c(0, 45)
                 ) +
  labs(title = "Waiting Time at a Given Stop (for Zip 20002)",
       x = "Hour Group",
       y = "Waiting Time (min)"
      )
  # facet_wrap(~Stop_Zip
  #            # nrow = 5
  #           )

WaitTime_AllBus_HG_Box

Violin plots for WaitTime (Zip Code, by HourGroupZip).


WaitTime_AllBus_HG_Vln <- ggplot(Zip,
                                 aes(factor(Event_Time_HrGroup),
                                     WaitTime_Min2,
                                     fill = factor(Event_Time_HrGroup)
                                    )
                                ) + 
  geom_violin(draw_quantiles = c(0.25, 0.5, 0.75),
              trim = TRUE,
              scale = "count",
              na.rm = TRUE,
              show.legend = NA,
              inherit.aes = TRUE
             ) +
  geom_text(data = CountValues_AllBus_HG,
            aes(y = Value_Counts,
                label = format(round(Value_Counts, digits = 1),
                               nsmall = 1
                              )
               ),
            size = 2.5,
            vjust = -0.5
           ) +
  theme(legend.position="none", axis.text.x = element_text(angle=45)) +
  coord_cartesian(# xlim = c(0, 180),
                  ylim = c(0, 90)
                 ) +
  labs(title = "Waiting Time at a Given Stop (for Zip 20002)",
       x = "Hour Group",
       y = "Waiting Time (min)"
      )
  # facet_wrap(~Stop_Zip
  #            # nrow = 5
  #           )

WaitTime_AllBus_HG_Vln

Box plots for WaitTime (Route, by HourGroupZip).


# Count_Values is needed to display the medians on the box plots
Rte <- select(WaitTime_RteCnts,
              Route,
              WaitTime_Min2,
              Stop_Zip,
              Event_Time_HrGroup
             ) %>% 
  filter(Route == "X2")

CountValues_AllBus_RteHG <- group_by(Rte,
                                     Event_Time_HrGroup
                                    ) %>% 
  summarise(
    Value_Counts = median(WaitTime_Min2,
                          na.rm = TRUE
                         ),
    VC = quantile(WaitTime_Min2, probs = 0.9, na.rm = TRUE)
    )


WaitTime_AllBus_RteHG_Box <- ggplot(Rte,
                                    aes(factor(Event_Time_HrGroup),
                                        WaitTime_Min2,
                                        fill = factor(Event_Time_HrGroup)
                                       )
                                   ) + 
  geom_boxplot(outlier.colour="red", notch=TRUE, na.rm = TRUE) +
  geom_text(data = CountValues_AllBus_RteHG,
            aes(y = Value_Counts,
                label = format(round(Value_Counts, digits = 1),
                               nsmall = 1
                              )
               ),
            size = 2.5,
            vjust = -0.5
           ) +
  theme(legend.position="none", axis.text.x = element_text(angle=45)) +
  coord_cartesian(# xlim = c(0, 180),
                  ylim = c(0, max(CountValues_AllBus_RteHG$VC))
                 ) +
  labs(title = "Waiting Time at a Given Stop",
       subtitle = ("Route X2"),
       x = "Hour Group",
       y = "Waiting Time (min)"
      ) 
# +
#   facet_wrap(~Stop_Zip
#              # nrow = 5
#             )

WaitTime_AllBus_RteHG_Box

Violin plots for WaitTime (Zip Code, by HourGroupZip).


WaitTime_AllBus_RteHG_Vln <- ggplot(Rte,
                                    aes(factor(Event_Time_HrGroup),
                                        WaitTime_Min2,
                                        fill = factor(Event_Time_HrGroup)
                                       )
                                   ) + 
  geom_violin(draw_quantiles = c(0.25, 0.5, 0.75),
              trim = TRUE,
              scale = "count",
              na.rm = TRUE,
              show.legend = NA,
              inherit.aes = TRUE
             ) +
  geom_text(data = CountValues_AllBus_RteHG,
            aes(y = Value_Counts,
                label = format(round(Value_Counts, digits = 1),
                               nsmall = 1
                              )
               ),
            size = 2.5,
            vjust = -0.5
           ) +
  theme(legend.position="none", axis.text.x = element_text(angle=45)) +
  coord_cartesian(# xlim = c(0, 180),
                  ylim = c(0, 45)
                 ) +
  labs(title = "Waiting Time at a Given Stop",
       subtitle = ("(Route X2)"),
       x = "Hour Group",
       y = "Waiting Time (min)"
      ) +
  facet_wrap(~Stop_Zip
             # nrow = 5
            )

WaitTime_AllBus_RteHG_Vln

X2 Percentiles Line Graph Test.


X2_Pct <- select(WaitTime_RteCnts,
                 Route,
                 Stop_Zip,
                 Event_Time_Date,
                 Event_Time_Day,
                 Event_Time_HrGroup,
                 Event_Time_Hr,
                 Latitude,
                 Longitude,
                 WaitTime_Min2
                ) %>% 
  filter(Route == "X2") %>% 
  group_by(Event_Time_Hr,
           Stop_Zip
          ) %>% 
  summarise(Pct50 = quantile(WaitTime_Min2, probs = 0.5, na.rm = TRUE),
            Pct60 = quantile(WaitTime_Min2, probs = 0.6, na.rm = TRUE),
            Pct70 = quantile(WaitTime_Min2, probs = 0.7, na.rm = TRUE),
            Pct80 = quantile(WaitTime_Min2, probs = 0.8, na.rm = TRUE),
            Pct90 = quantile(WaitTime_Min2, probs = 0.9, na.rm = TRUE)
           )

str(X2_Pct)
View(X2_Pct)


X2_Long <- gather(X2_Pct,
                  key = Percentile,
                  value = Pctile,
                  Pct50,
                  Pct60,
                  Pct70,
                  Pct80,
                  Pct90
                )

str(X2_Long)
View(X2_Long)


X2_WaitByHr_Line <- ggplot(X2_Long,
                           aes(x = Event_Time_Hr,
                               y = Pctile,
                               factor(Percentile),
                               color = Percentile
                              )
                          ) +
  geom_line() +
  theme(legend.title=element_blank(),
        legend.position = "bottom"
       ) +
  coord_cartesian(xlim = c(0, 23)
                  # ylim = c(0, 45)
                 ) + 
  scale_x_continuous(breaks = seq(0, 23, 2)
                    ) +
  labs(title = "Waiting Time Throughout the Day",
       subtitle = ("(Route X2)"),
       x = "Hour of the Day",
       y = "Waiting Time (min)"
      ) +
  facet_wrap(~Stop_Zip)

X2_WaitByHr_Line

GET DATA READY FOR SHINY – GET DATA READY FOR SHINY – GET DATA READY FOR SHINY GET DATA READY FOR SHINY – GET DATA READY FOR SHINY – GET DATA READY FOR SHINY GET DATA READY FOR SHINY – GET DATA READY FOR SHINY – GET DATA READY FOR SHINY

BaseData: Used in plots by hour and zipcode (first two Shiny tabs).


# str(WaitTime_RteCnts)

Shiny_WaitData_Base <- select(WaitTime_RteCnts,
                              Route,
                              Stop_Zip,
                              Event_Time,
                              Event_Time_Date,
                              Event_Time_Day,
                              Event_Time_HrGroup,
                              Event_Time_Hr,
                              Latitude,
                              Longitude,
                              WaitTime_Min2
                             ) %>% 
  mutate(Event_Time_YrMthDayHr = floor_date(Event_Time, "hour")
        ) %>% 
  rename(ZipCode = Stop_Zip,
         HourGroup = Event_Time_HrGroup,
         Date = Event_Time_Date,
         Day = Event_Time_Day,
         Hour = Event_Time_Hr,
         WaitTime_Min = WaitTime_Min2
        ) %>% 
  filter(WaitTime_Min <= 180)

Shiny_WaitData_Base$Route <- factor(Shiny_WaitData_Base$Route)

str(Shiny_WaitData_Base)
View(tail(Shiny_WaitData_Base, 500))

saveRDS(Shiny_WaitData_Base,
        "Shiny_WaitData_Base.rds"
       )

Prep data for mapping.


# devtools::install_github("dkahle/ggmap")
# devtools::install_github("hadley/ggplot2")
# install.packages("ggmap", type = "source")

# devtools::install_github('hadley/ggplot2')
devtools::install_github("hadley/ggplot2@v2.2.0")
# devtools::install_github('thomasp85/ggforce')
# devtools::install_github('thomasp85/ggraph')
# devtools::install_github('slowkow/ggrepel')


tract <- 
  readOGR(dsn = "/Users/mdturse/Desktop/Analytics/DCMetroBus/tl_2016_us_zcta510",
          layer = "tl_2016_us_zcta510"
         )
  
class(tract)

# convert the GEOID to a character
tract@data$GEOID <- as.character(tract@data$GEOID)
str(tract@data)


ggtract <- tidy(tract, region = "GEOID")

# str(ggtract)
# summary(ggtract)
# View(head(ggtract, 50))



# str(Shiny_WaitData_Base)

ZipWaitTest <- filter(Shiny_WaitData_Base,
                      WaitTime_Min <= 180 &
                        !is.na(ZipCode)
                     ) %>% 
  group_by(ZipCode,
           Event_Time_YrMthDayHr
           # Event_Time_Day,
           # Event_Time_Hr
          ) %>% 
  summarise(Pct80 = quantile(WaitTime_Min, probs = 0.8, na.rm = TRUE)
           ) %>% 
  arrange(# Event_Time_Hr,
          ZipCode,
          Event_Time_YrMthDayHr
         ) %>% 
  as.data.frame() %>% 
  mutate(Event_Time_DateNew = floor_date(Event_Time_YrMthDayHr, "day"),
         Event_Time_HrNew = hour(Event_Time_YrMthDayHr),
         Pct80_Level = factor(ifelse(Pct80 < 10,
                                     "Below 10",
                              ifelse(Pct80 < 20,
                                     "Below 20",
                              ifelse(Pct80 < 30,
                                     "Below 30",
                              ifelse(Pct80 < 40,
                                     "Below 40",
                              ifelse(Pct80 < 50,
                                     "Below 50",
                              ifelse(Pct80 < 60,
                                     "Below 60",
                                     "60 Plus"
                                    )))))),
                              levels = c("Below 10", "Below 20", "Below 30", 
                                         "Below 40", "Below 50", "Below 60", "60 Plus"
                                        ),
                              ordered = TRUE
                             )
        )

str(ZipWaitTest)
ZipWaitTest$ZipCode <- as.character(ZipWaitTest$ZipCode)
str(ZipWaitTest)
summary(ZipWaitTest)

View(head(ZipWaitTest, 500))


StopZip_Left <- left_join(ZipWaitTest,
                          ggtract,
                          by = c("ZipCode" = "id")
                         )

str(StopZip_Left)
summary(StopZip_Left)

Test mapping functionaltiy.


map <- get_map(location = c(lon = -77.03676, lat = 38.89784),
               source = "google",
               # maptype = "roadmap"
               zoom = 12
              )

ggmap(map) +
  geom_polygon(aes(x = long, 
                   y = lat, 
                   group = group,
                   fill = Pct80_Level
                  ), 
               data = filter(StopZip_Left,
                             Event_Time_YrMthDayHr == as.POSIXct("2016-10-07 20:00:00")
                             # &
                             #   Stop_Zip == "20003"
                            ),
               colour = "gray1", 
               # fill = 'black', 
               alpha = .4, 
               size = .3
              ) +
# +
  # scale_fill_gradientn(colours = c("white", "royalblue4", "red"),
  #                      #  "lightsteelblue4",
  #                      # "lightpink1",
  #                      # values=cbPalette,
  #                      # values = c(1,0.5, .3, .2, .1, 0)
  #                      na.value = "black",
  #                      breaks = c(seq(0, 180, 30))
  #                      # values = rescale()
  #                     ) 
# +
  scale_fill_brewer(palette = "Spectral", # "YlOrRd" # "Set1",
                    direction = -1,
                    limits = levels(StopZip_Left$Pct80_Level)
                   )

Shiny data for mapping (used in 3rd tab).


View(head(filter(StopZip_Left,
                 Event_Time_HrNew == 15
                ),
          500
         )
    )

Shiny_WaitData_Map <- StopZip_Left %>% 
  rename(YrMthDayHr = Event_Time_YrMthDayHr,
         YrMthDay = Event_Time_DateNew,
         Hour = Event_Time_HrNew
        )

str(Shiny_WaitData_Map)


Shiny_WaitData_Map_Wed <- filter(Shiny_WaitData_Map,
                                 YrMthDay == as.POSIXct("2016-10-05")
                                )

str(Shiny_WaitData_Map_Wed)
summary(Shiny_WaitData_Map_Wed)


saveRDS(Shiny_WaitData_Map,
        "Shiny_WaitData_Map.rds"
       )

saveRDS(Shiny_WaitData_Map_Wed,
        "Shiny_WaitData_Map_Wed.rds"
       )

Clustering

Data prep.

str(RouteStats)
'data.frame':   268 obs. of  43 variables:
 $ Route               : chr  "10A" "10B" "10E" "11Y" ...
 $ BusDayEventNum_Mean : num  272 297 161 157 229 ...
 $ BusDayEventNum_Pct10: num  59 75 13.2 13 29 ...
 $ BusDayEventNum_Pct25: num  134 154 34 36 119 78 141 109 38 139 ...
 $ BusDayEventNum_Pct50: num  257 280 131 87 222 104 236 264 94 239 ...
 $ BusDayEventNum_Pct75: num  387 415 265 228 328 ...
 $ BusDayEventNum_Pct90: num  484 536 368 341 454 ...
 $ StopSequence_Mean   : num  28.1 35.1 23.4 25.8 22.8 ...
 $ StopSequence_Pct10  : num  6 8 4 5 5 9 7 5 5 4 ...
 $ StopSequence_Pct25  : num  15 18 12 12 12 20 16 10 10 8 ...
 $ StopSequence_Pct50  : num  28 35 24 24 23 37 31 19 21 14 ...
 $ StopSequence_Pct75  : num  42 52 35 40 34 52 46 28 32 21 ...
 $ StopSequence_Pct90  : num  50 62 42 49 40 61 55 35 43.7 28 ...
 $ EventTimeHr_Mean    : num  13.1 13.2 13.7 14.7 13.4 ...
 $ EventTimeHr_Pct10   : num  5 6 6 7 6 6 7 6 0 6 ...
 $ EventTimeHr_Pct25   : num  8 9 7 8 8 7 9 7 0 9 ...
 $ EventTimeHr_Pct50   : num  13 13 17 17 16 8 13 16 1 14 ...
 $ EventTimeHr_Pct75   : num  18 18 18 18 18 8 18 21 23 18 ...
 $ EventTimeHr_Pct90   : num  21 21 18 19 19 9 20 22 23 21 ...
 $ DwellTime2_Mean     : num  3.82 5.48 3.58 4.31 4.16 ...
 $ DwellTime2_Pct10    : num  0 0 0 0 0 0 0 0 0 0 ...
 $ DwellTime2_Pct25    : num  0 0 0 0 0 0 0 0 0 0 ...
 $ DwellTime2_Pct50    : num  0 0 0 0 0 0 0 0 0 0 ...
 $ DwellTime2_Pct75    : num  2 3 0 6 0 0 4 3 1 4 ...
 $ DwellTime2_Pct90    : num  8 10 7 14 3 3 10 8 5 10 ...
 $ TravDistMi_Mean     : num  0.238 0.227 0.318 0.483 0.291 ...
 $ TravDistMi_Pct10    : num  0.1061 0.1009 0.106 0.1071 0.0966 ...
 $ TravDistMi_Pct25    : num  0.143 0.138 0.151 0.163 0.13 ...
 $ TravDistMi_Pct50    : num  0.195 0.187 0.203 0.25 0.21 ...
 $ TravDistMi_Pct75    : num  0.27 0.255 0.262 0.45 0.288 ...
 $ TravDistMi_Pct90    : num  0.379 0.362 0.405 0.837 0.41 ...
 $ TravTimSec_Mean     : num  83.7 71.9 183.9 260.1 101.1 ...
 $ TravTimSec_Pct10    : num  15 14 18 17 12 11 16 19 16 24 ...
 $ TravTimSec_Pct25    : num  26 22 28 29 16 ...
 $ TravTimSec_Pct50    : num  42 37 43 62 23 ...
 $ TravTimSec_Pct75    : num  69 66.2 77 118 46 ...
 $ TravTimSec_Pct90    : num  110 112 124 188 116 ...
 $ WaitTimMin_Mean     : num  26.6 26.1 43.4 45.9 40.6 ...
 $ WaitTimMin_Pct10    : num  3.47 3.63 2.73 3.89 4.18 ...
 $ WaitTimMin_Pct25    : num  8.74 9.03 6.42 9.6 10.78 ...
 $ WaitTimMin_Pct50    : num  17.8 18.4 14.1 22.1 22 ...
 $ WaitTimMin_Pct75    : num  28.5 28.9 73.3 68.7 45.3 ...
 $ WaitTimMin_Pct90    : num  53.1 52.2 144.4 136.9 123.3 ...

PCA

Using caret::preProcess.

PCA

Using stats::prcomp.

Clustering (using the Principal Components computed using caret::preProcess).

Are the data clusterable?

##### Are the data clusterable?
# gradient_col <- list(low = "steelblue", high = "white")
ClustData_Ends <- get_clust_tendency(RouteStats_Pca,
                                     n = nrow(RouteStats_Pca
                                             ) - 1,
                                     # gradient = gradient_col,
                                     seed = 123456789
                                    )
str(ClustData_Ends)
List of 2
 $ hopkins_stat: num 0.166
 $ plot        :List of 9
  ..$ data       :'data.frame': 71824 obs. of  3 variables:
  .. ..$ Var1 : Factor w/ 268 levels "r202-","r210-",..: 1 2 3 4 5 6 7 8 9 10 ...
  .. ..$ Var2 : Factor w/ 268 levels "r202-","r210-",..: 1 1 1 1 1 1 1 1 1 1 ...
  .. ..$ value: num [1:71824] 0 13.7 11.4 10.6 12.9 ...
  ..$ layers     :List of 1
  .. ..$ :Classes 'LayerInstance', 'Layer', 'ggproto' <ggproto object: Class LayerInstance, Layer>
    aes_params: list
    compute_aesthetics: function
    compute_geom_1: function
    compute_geom_2: function
    compute_position: function
    compute_statistic: function
    data: waiver
    draw_geom: function
    finish_statistics: function
    geom: <ggproto object: Class GeomTile, GeomRect, Geom>
        aesthetics: function
        default_aes: uneval
        draw_group: function
        draw_key: function
        draw_layer: function
        draw_panel: function
        extra_params: na.rm width height
        handle_na: function
        non_missing_aes: 
        optional_aes: 
        parameters: function
        required_aes: x y
        setup_data: function
        use_defaults: function
        super:  <ggproto object: Class GeomRect, Geom>
    geom_params: list
    inherit.aes: TRUE
    layer_data: function
    map_statistic: function
    mapping: uneval
    position: <ggproto object: Class PositionIdentity, Position>
        compute_layer: function
        compute_panel: function
        required_aes: 
        setup_data: function
        setup_params: function
        super:  <ggproto object: Class Position>
    print: function
    show.legend: NA
    stat: <ggproto object: Class StatIdentity, Stat>
        aesthetics: function
        compute_group: function
        compute_layer: function
        compute_panel: function
        default_aes: uneval
        extra_params: na.rm
        finish_layer: function
        non_missing_aes: 
        parameters: function
        required_aes: 
        retransform: TRUE
        setup_data: function
        setup_params: function
        super:  <ggproto object: Class Stat>
    stat_params: list
    subset: NULL
    super:  <ggproto object: Class Layer> 
  ..$ scales     :Classes 'ScalesList', 'ggproto' <ggproto object: Class ScalesList>
    add: function
    clone: function
    find: function
    get_scales: function
    has_scale: function
    input: function
    n: function
    non_position_scales: function
    scales: list
    super:  <ggproto object: Class ScalesList> 
  ..$ mapping    :List of 2
  .. ..$ x: symbol Var1
  .. ..$ y: symbol Var2
  ..$ theme      :List of 4
  .. ..$ axis.title.x: list()
  .. .. ..- attr(*, "class")= chr [1:2] "element_blank" "element"
  .. ..$ axis.title.y: list()
  .. .. ..- attr(*, "class")= chr [1:2] "element_blank" "element"
  .. ..$ axis.text   : list()
  .. .. ..- attr(*, "class")= chr [1:2] "element_blank" "element"
  .. ..$ axis.ticks  : list()
  .. .. ..- attr(*, "class")= chr [1:2] "element_blank" "element"
  .. ..- attr(*, "class")= chr [1:2] "theme" "gg"
  .. ..- attr(*, "complete")= logi FALSE
  .. ..- attr(*, "validate")= logi FALSE
  ..$ coordinates:Classes 'CoordCartesian', 'Coord', 'ggproto' <ggproto object: Class CoordCartesian, Coord>
    aspect: function
    distance: function
    expand: TRUE
    is_linear: function
    labels: function
    limits: list
    range: function
    render_axis_h: function
    render_axis_v: function
    render_bg: function
    render_fg: function
    train: function
    transform: function
    super:  <ggproto object: Class CoordCartesian, Coord> 
  ..$ facet      :Classes 'FacetNull', 'Facet', 'ggproto' <ggproto object: Class FacetNull, Facet>
    compute_layout: function
    draw_back: function
    draw_front: function
    draw_labels: function
    draw_panels: function
    finish_data: function
    init_scales: function
    map: function
    map_data: function
    params: list
    render_back: function
    render_front: function
    render_panels: function
    setup_data: function
    setup_params: function
    shrink: TRUE
    train: function
    train_positions: function
    train_scales: function
    vars: function
    super:  <ggproto object: Class FacetNull, Facet> 
  ..$ plot_env   :<environment: 0x200b876d8> 
  ..$ labels     :List of 3
  .. ..$ x   : chr "Var1"
  .. ..$ y   : chr "Var2"
  .. ..$ fill: chr "value"
  ..- attr(*, "class")= chr [1:2] "gg" "ggplot"
# Hopkins statistic
ClustData_Ends$hopkins_stat  # value of 0.1657494 implies that the data are not uniformly distributed (they are "clusterable")
[1] 0.1657494
#plot
ClustData_Ends$plot

Clustering. How many clusters are there?

kmeans, pam, and hierarchical clustring methods, using within sum of squares and silhouette measures.

fviz_nbclust(RouteStats_Pca, kmeans, method = "wss")  # ~8 clusters
fviz_nbclust(RouteStats_Pca, pam, method = "wss")  # ~6 clusters

fviz_nbclust(RouteStats_Pca, hcut, method = "wss")  # ~6 clusters

fviz_nbclust(RouteStats_Pca, kmeans, method = "silhouette")  # 2 clusters

fviz_nbclust(RouteStats_Pca, pam, method = "silhouette")  # 2 clusters

fviz_nbclust(RouteStats_Pca, hcut, method = "silhouette",
             hc_method = "complete")  # 2 clusters

Clustering. How many clusters are there?

kmeans method with the gap statistic, using bootstrap.

# Plot kmeans
fviz_gap_stat(gap_stat_km, 
              maxSE = list(method = "Tibs2001SEmax")
             )  # 1 cluster
# Plot pam
fviz_gap_stat(gap_stat_pm, 
              maxSE = list(method = "Tibs2001SEmax")
             )  # 2 cluster

# Plot hierarchical
fviz_gap_stat(gap_stat_hcut, 
              maxSE = list(method = "Tibs2001SEmax")
             )  # 1 cluster

Clustering. How many clusters are there?

kmeans method with various different statistics.

fviz_nbclust(nb)
Among all indices: 
===================
* 2 proposed  0 as the best number of clusters
* 1 proposed  1 as the best number of clusters
* 7 proposed  2 as the best number of clusters
* 2 proposed  3 as the best number of clusters
* 1 proposed  5 as the best number of clusters
* 1 proposed  6 as the best number of clusters
* 1 proposed  7 as the best number of clusters
* 6 proposed  8 as the best number of clusters
* 2 proposed  9 as the best number of clusters
* 1 proposed  13 as the best number of clusters
* 2 proposed  15 as the best number of clusters

Conclusion
=========================
* According to the majority rule, the best number of clusters is  2 .

Clustering. How many clusters are there?

Hierarchical clustering method. Particularly looking at silhouette statistics.

# Visualize
HCDend_K2
HCDend_K3

HCDend_K4

HCDend_K5

HCDend_K6

HCDend_K7

HCDend_K8

HCDend_K9

HCDend_K10

HCDend_K11

HCDend_K12

HCDend_K13

HCDend_K14

HCDend_K15

HCSil_K2

HCSil_K3

HCSil_K4

HCSil_K5

HCSil_K6

HCSil_K7

HCSil_K8

HCSil_K9

HCSil_K10

HCSil_K11

HCSil_K12

HCSil_K13

HCSil_K14

HCSil_K15

HCSilWidth_AllK

Using kmeans, PAM, and Hierarchical clustering methods, we can say we probably have aroun 2 clusters.

Let’s try density clustering. (This tends to show that maybe there is only one “cluster,” meaning that data are not clusterable.)

# Compute DBSCAN using fpc package
kNNdistplot(RouteStats_Pca, k = 10)
abline(h = 8.5, lty = 2)
set.seed(123456789)
db <- fpc::dbscan(RouteStats_Pca,
                  eps = 8.5,
                  MinPts = 10
                )
str(db)
List of 4
 $ cluster: num [1:268] 1 1 1 1 1 1 1 1 1 1 ...
 $ eps    : num 8.5
 $ MinPts : num 10
 $ isseed : logi [1:268] TRUE TRUE TRUE TRUE TRUE TRUE ...
 - attr(*, "class")= chr "dbscan"
db
dbscan Pts=268 MinPts=10 eps=8.5
       0   1
border 5   7
seed   0 256
total  5 263
# Plot DBSCAN results
fviz_cluster(db,
             RouteStats_Pca,
             stand = FALSE,
             frame = FALSE,
             geom = "point"
            )
argument frame is deprecated; please use ellipse instead.

We can say that MAYBE there are two clusters, but there is more evidence for probably just one cluster (i.e., the data are NOT clusterable).

# remove no longer needed items
rm(X2_Long, X2_Pct, ClustData_Ends, db, gap_stat, gap_stat_hcut, gap_stat_km, gap_stat_pm, i, nb, rd, Trnsfrm, x, y)

Investigating TravelTime_Sec.


View(filter(TTLargeRteChng,
            !is.na(TravelTime_Sec) &
              RteChange2 == "Same"
           ) %>% 
       arrange(desc(TravelTime_Sec),
               SpeedAvg_Mph_NewHvrs
              ) %>%
       head(500)
    )


# examples where TravelTime_Sec is small (1 sec) and SpeedAvg_Mph_NewHvrs is large.
View(select(NewTravTime,
            # -matches("(q(2|5|(95)|(98)))|Mean|Med|Cnt")
            -(TD_Mi_q2:TD_Mi_SSHG_Cnt_F),
            -(TT_Hr_q2:TT_Hr_SSHG_Cnt_F)
           ) %>% 
       filter((RowNum_OG >= 2217353 & RowNum_OG <= 2217373) | # 2217363
                (RowNum_OG >= 3090321 & RowNum_OG <= 3090341) | # 3090331
                (RowNum_OG >= 80764 & RowNum_OG <= 80784) | # 80774
                (RowNum_OG >= 33840 & RowNum_OG <= 33860) # 33850
           )
    )






# examples where TravelTime_Sec is large and SpeedAvg_Mph_NewHvrs is small.
View(filter(TTLargeRteChng,
            (RowNum_OG >= 2250290 & RowNum_OG <= 2250310) | # 2250300
              (RowNum_OG >= 867717 & RowNum_OG <= 867737) | # 867727
              (RowNum_OG >= 864379 & RowNum_OG <= 864399) | # 864389
              (RowNum_OG >= 808395 & RowNum_OG <= 808415) # 808405
           )
    )

         
         
# examples where TravelTime_Sec is unusually small (with TravelDistance_Mi values that are large).
View(filter(AllDays_NewTravelDist,
            (RowNum_OG >= 1042228 & RowNum_OG <= 1042248) | # 1042238
                (RowNum_OG >= 53816 & RowNum_OG <= 53836) | # 53826
                (RowNum_OG >= 360571 & RowNum_OG <= 360591) | # 360581
                (RowNum_OG >= 502271 & RowNum_OG <= 502291) # 502281 (can't explian the weird TravelTime_Sec calculation here - it's not even an integer!)
           )
    )

# still trying to explain 502281...on the day of this weirdness, the bus was only in circulation for 4-5 stops (~20 minutes) on that day (Oct 6)
View(filter(AllDays_NewTravelDist,
            Bus_ID == 2711
           )
    )


# exploring large values for TravelTime_Sec
View(filter(AllDays_NewTravelDist,
            TravelTime_Sec == 300
           ) %>% 
       arrange(desc(TravelTime_Sec),
               SpeedAvg_Mph2
              )
    )

# examples where TravelTime_Sec is unusually large (with TravelDistance_Mi values that are small, so SpeedAvg_Mph values are very small).
View(filter(AllDays_NewTravelDist,
            (RowNum_OG >= 2627459 & RowNum_OG <= 2627479) | # 2627469
                (RowNum_OG >= 2193344 & RowNum_OG <= 2193364) | # 2193354
                (RowNum_OG >= 1644123 & RowNum_OG <= 1644143) | # 1644133
                (RowNum_OG >= 869600 & RowNum_OG <= 869620) # 869610
           )
    )

Investigation of SpeedAvg_Mph2

View(Speed_Pctiles): 90% of SpeedAvg_Mph2 are between ~3mph and ~66mph.


Speed_Ntile <- as.data.frame(AllDays_NewTravelDist$SpeedAvg_Mph2) %>% 
  mutate(Pctile = ntile(AllDays_NewTravelDist$SpeedAvg_Mph2, 100),
         MinR = min_rank(AllDays_NewTravelDist$SpeedAvg_Mph2),
         PctR = percent_rank(AllDays_NewTravelDist$SpeedAvg_Mph2),
         PctR_Round = round(PctR, 2)
        ) 

colnames(Speed_Ntile)[1] <- "SpeedAvg_Mph2"
str(Speed_Ntile)

Speed_Ntile_Rows <- nrow(Speed_Ntile)

View(tail(Speed_Ntile, 500))


Speed_Pctiles <- group_by(Speed_Ntile,
                          PctR_Round
                         ) %>% 
  summarise(
    MinSpeedAtPctile = min(SpeedAvg_Mph2),
    CntsAtPctile = n(),
    PctsAtPctile = CntsAtPctile / Speed_Ntile_Rows
  ) %>% 
  mutate(CumSumPAtP = cumsum(PctsAtPctile)
        )

View(Speed_Pctiles)

Investigation of SpeedAvg_Mph2.

Exploring the removal of outlier TravelTime_Sec and TravelDistance_Mi.


summary(select(AllDays_NewTravelDist,
               SpeedAvg_Mph,
               SpeedAvg_Mph2
              )
       )

summary(select(filter(AllDays_NewTravelDist,
                      TravelDistance_Mi > 0.0001893939 & # lowest non-zero percentile
                        TravelDistance_Mi < 1.0812500000 & # 99th percentile
                        TravelTime_Sec > 10.050000 & # 2nd percentile
                        TravelTime_Sec < 293.000000 # 98th percentile
                     ),
               SpeedAvg_Mph,
               SpeedAvg_Mph2
              )
       )

Investigation of SpeedAvg_Mph2.

Histogram of SpeedAvg_Mph2.


Speed_HistDen <- ggplot(filter(AllDays_NewTravelDist,
                               !is.na(SpeedAvg_Mph2)
                              ),
                        aes(x = SpeedAvg_Mph2,
                            y = ..density..
                           )
                       ) +
  geom_histogram(binwidth = 5, fill = "lightblue", colour = "grey60", size = 0.2) +
  geom_line(stat = "density", colour = "red") +
  stat_bin(binwidth = 5,
           geom = "text",
           size = 2.5,
           vjust = 1.5,
           aes(label = format(..count.., big.mark = ",")
              ),
          ) +
  # geom_text(aes(label = format(..count.., big.mark = ",")
  #              ),
  #           size = 3,
  #           nudge_y = (..count.. * 0.1)
  #          ) +
  coord_cartesian(xlim = c(0, 70), ylim = c(0, 0.04)
                 ) +
  #  theme(legend.position="none") +
  labs(title = "Variation in Travel Speed",
       x = "Average Speed (mph)",
       y = "Density"
      )

Speed_HistDen

Investigation of SpeedAvg_Mph2.

Histogram of SpeedAvg_Mph2 after removing outlier TravelTime_Sec and TravelDistance_Mi.


View(TravDistMiNew_Pctiles)
View(TravTimeHr_Pctiles)

SpeedNoOutlier_HistDen <- ggplot(filter(AllDays_NewTravelDist,
                                        !is.na(SpeedAvg_Mph2) &
                                          TravelDistance_Mi_New > 0.077841005 & # 5th percentile
                                          # TravelDistance_Mi_New < 1.0812500000 & # 99th percentile
                                          TravelTime_Sec > 12.100000 # 4th percentile
                                          # TravelTime_Sec < 293.000000 # 98th percentile
                                       ),
                                 aes(x = SpeedAvg_Mph2,
                                     y = ..density..
                                    )
                                ) +
  geom_histogram(binwidth = 5, fill = "lightblue", colour = "grey60", size = 0.2) +
  geom_line(stat = "density", colour = "red") +
  stat_bin(binwidth = 5,
           geom = "text",
           size = 2.5,
           vjust = 1.5,
           aes(label = format(..count.., big.mark = ",")
              ),
          ) +
  # geom_text(aes(label = format(..count.., big.mark = ",")
  #              ),
  #           size = 3,
  #           nudge_y = (..count.. * 0.1)
  #          ) +
  coord_cartesian(xlim = c(0, 70), ylim = c(0, 0.04)
                 ) +
  #  theme(legend.position="none") +
  labs(title = "Variation in Travel Speed",
       subtitle = "(removed low outliers of Travel Distance and Travel Time)",
       x = "Average Speed (mph)",
       y = "Density"
      )

SpeedNoOutlier_HistDen

Investigation of SpeedAvg_Mph2.

New dataset (NoOutliers_TravelDistNTime) when removing outlier low values of TravelDistance_Mi_New and TravelTime_Sec.


View(TravDistMiNew_Pctiles)
View(TravTimeHr_Pctiles)

NoOutliers_TravelDistNTime <- filter(AllDays_NewTravelDist,
                                     TravelDistance_Mi_New > .077841005 & # 5th percentile
                                       # TravelDistance_Mi_New < 1.0812500000 & # 99th percentile
                                       TravelTime_Sec > 12.100000 # 4th percentile
                                       # TravelTime_Sec < 293.000000 # 98th percentile
                                    )

nrow(AllDays_NewTravelDist) - nrow(NoOutliers_TravelDistNTime)

str(NoOutliers_TravelDistNTime)
summary(NoOutliers_TravelDistNTime)

Investigation of SppedAvg_Mph2.

View(Speed_NoOut_Pctiles): Aproximately 90% of SpeedAvg_Mph2 values are between ~4mph and ~56mph.


Speed_NoOut_Ntile <- as.data.frame(NoOutliers_TravelDistNTime$SpeedAvg_Mph2) %>% 
  mutate(Pctile = ntile(NoOutliers_TravelDistNTime$SpeedAvg_Mph2, 100),
         MinR = min_rank(NoOutliers_TravelDistNTime$SpeedAvg_Mph2),
         PctR = percent_rank(NoOutliers_TravelDistNTime$SpeedAvg_Mph2),
         PctR_Round = round(PctR, 2)
        ) 

colnames(Speed_NoOut_Ntile)[1] <- "SpeedAvg_Mph2"
str(Speed_NoOut_Ntile)

Speed_NoOut_Ntile_Rows <- nrow(Speed_NoOut_Ntile)

View(tail(Speed_NoOut_Ntile, 500))


Speed_NoOut_Pctiles <- group_by(Speed_NoOut_Ntile,
                                PctR_Round
                               ) %>% 
  summarise(
    MinSpeedAtPctile = min(SpeedAvg_Mph2),
    CntsAtPctile = n(),
    PctsAtPctile = CntsAtPctile / Speed_NoOut_Ntile_Rows
  ) %>% 
  mutate(CumSumPAtP = cumsum(PctsAtPctile)
        )

View(Speed_NoOut_Pctiles)

Investigation of SppedAvg_Mph2.

Exloring odd/impossible values.


# Exploring when SpeedAvg_Mph2 is NA  --  does not occur at all
nrow(filter(NoOutliers_TravelDistNTime,
            is.na(SpeedAvg_Mph2)
           )
    )


# Exploring when SpeedAvg_Mph2 is zero  --  does not occur at all
nrow(filter(NoOutliers_TravelDistNTime,
            SpeedAvg_Mph2 == 0
           )
    )


# examples where SpeedAvg_Mph2 < 3.2848770
View(filter(AllDays_NewTravelDist,
            SpeedAvg_Mph2 > 0 &
              SpeedAvg_Mph2 < 3.2848770
           ) %>% 
       arrange(SpeedAvg_Mph2)
    )

# examples where SpeedAvg_Mph2 < 3.2848770
View(filter(AllDays_NewTravelDist,
            (RowNum_OG >= 485338 & RowNum_OG <= 485358) | # 485348  --  Extreme travel time, Route Change
                (RowNum_OG >= 346952 & RowNum_OG <= 346972) | # 346962  -- Extreme travel time, Route Change 
                (RowNum_OG >= 70494 & RowNum_OG <= 70514) | # 70504  --  Extreme travel time, Route Change
                (RowNum_OG >= 2051846 & RowNum_OG <= 2051866) # 2051856  --  Extreme travel time, Route Change
           )
    )

Investigation of SpeedAvg_Mph2.

Limit the dataset based on SpeedAvg_Mph2.


NoOutliersSpeed <- filter(NoOutliers_TravelDistNTime,
                          between(SpeedAvg_Mph2,
                                  4.069300, # 5th percentile
                                  56.05651 #95th percentile
                                 )
                          )

nrow(NoOutliers_TravelDistNTime) - nrow(NoOutliersSpeed)

summary(NoOutliersSpeed)

TravelTime now looks like it has some odd values on the high end. So let’s look at those.

View(TravTime_NoOut_Pctiles): Virtually all trips should take less than 5 minutes. (The 99th percentile of of TravelTime is approximately 8 minutes.)


TravTime_NoOut_Ntile <- as.data.frame(NoOutliersSpeed$TravelTime_Hr) %>% 
  mutate(Pctile = ntile(NoOutliersSpeed$TravelTime_Hr, 100),
         MinR = min_rank(NoOutliersSpeed$TravelTime_Hr),
         PctR = percent_rank(NoOutliersSpeed$TravelTime_Hr),
         PctR_Round = round(PctR, 2)
        )

colnames(TravTime_NoOut_Ntile)[1] <- "TravelTime_Hr"
str(TravTime_NoOut_Ntile)

TravTime_NoOut_Ntile_Rows <- nrow(TravTime_NoOut_Ntile)

View(tail(TravTime_NoOut_Ntile, 500))


TravTime_NoOut_Pctiles <- group_by(TravTime_NoOut_Ntile,
                                   PctR_Round
                                  ) %>% 
  summarise(
    MinTravTimeHrAtPctile = min(TravelTime_Hr),
    CntsAtPctile = n(),
    PctsAtPctile = CntsAtPctile / TravTime_NoOut_Ntile_Rows
  ) %>% 
  mutate(CumSumPAtP = cumsum(PctsAtPctile),
         MinTravTimeSecAtPctile = MinTravTimeHrAtPctile * (60 * 60)
        )

View(TravTime_NoOut_Pctiles)

Investigating odd TravelTime_Sec values.

Trips longer than ~8 minutes.


View(filter(NoOutliersSpeed,
            TravelTime_Sec > 491 # min at the 100th percentile
           ) %>% 
       arrange(desc(TravelTime_Sec)
              )
    )

# examples of TravelTime_Sec values that are largest.
View(filter(NoOutliersSpeed,
            (RowNum_OG >= 2071759 & RowNum_OG <= 2071779) | # 2071769  --  results from a route change, and a 3hr+ wait before the new route starts
                (RowNum_OG >= 1473686 & RowNum_OG <= 1473706) | # 1473696  --  results from a route change, and a 3hr wait before the new route starts
                (RowNum_OG >= 1222822 & RowNum_OG <= 1222842) | # 1222832  --  results from a route change, and a 3hr wait before the new route starts
                (RowNum_OG >= 3046089 & RowNum_OG <= 3046109) # 3046099  --  results from a route change, and a 3hr wait before the new route starts
           )
    )


# examples of TravelTime_Sec values that are the smallest of the large.
View(filter(NoOutliersSpeed,
            (RowNum_OG >= 3044689 & RowNum_OG <= 3044709) | # 3044699  --  results from a route change
                (RowNum_OG >= 3022358 & RowNum_OG <= 3022378) | # 3022368  --  results from a route change
                (RowNum_OG >= 2993016 & RowNum_OG <= 2993036) | # 2993026  --  results from a previous route change (change occurred in deleted row)
                (RowNum_OG >= 2683703 & RowNum_OG <= 2683723) # 2683713  --  results from a previous route change (change occurred in deleted row)
           )
    )

Let’s look at the TravelTime_Sec values and route changes (DirChange2).

The 99th percentile of TravelTime_Sec for both, all trips, and just those trips NOT involving route changes (DirChange2 = “Same”), is approximately 5min (300 sec).

Nota Bene: The percentile calculation here is defined slightly different than in most of the above analyses (which get the lowest value in the bin created by 100 ntiles).


summary(select(NoOutliersSpeed,
               TravelTime_Sec
              )
       )

summary(select(filter(NoOutliersSpeed,
                      DirChange2 == "Same"
                     ),
               TravelTime_Sec
              )
       )

summary(select(filter(NoOutliersSpeed,
                      DirChange2 == "Change"
                     ),
               TravelTime_Sec
              )
       )


TravTimeSec_Qtiles_df <- data.frame(PctValue = seq(0, 100, 1),
                                    All = seq(1, 101, 1),
                                    Same = seq(1, 101, 1),
                                    Change = seq(1, 101, 1)
                                   )

TravTimeSec_Qtiles_df[ , 2] <- quantile(select(NoOutliersSpeed,
                                               TravelTime_Sec
                                              ),
                                        probs = seq(0, 1, 0.01),
                                        na.rm = TRUE
                                       )

TravTimeSec_Qtiles_df[ , 3] <- quantile(select(filter(NoOutliersSpeed,
                                                      DirChange2 == "Same"
                                                     ),
                                               TravelTime_Sec
                                              ),
                                        probs = seq(0, 1, 0.01),
                                        na.rm = TRUE
                                       )

TravTimeSec_Qtiles_df[ , 4] <- quantile(select(filter(NoOutliersSpeed,
                                                      DirChange2 == "Change"
                                                     ),
                                               TravelTime_Sec
                                              ),
                                        probs = seq(0, 1, 0.01),
                                        na.rm = TRUE
                                       )

View(TravTimeSec_Qtiles_df)

Limit the dataset now based on TravelTime_Sec.


UpperLimitTravTime <- filter(NoOutliersSpeed,
                             TravelTime_Sec <= 491 # min at the 100th percentile
                             )

nrow(NoOutliersSpeed) - nrow(UpperLimitTravTime)

str(UpperLimitTravTime)

summary(UpperLimitTravTime)

Investigation of Dwell_Time2 (how long the bus is at a stop).

Differences between Dwell_Time (by WMATA) and Dwell_Time2 (by me) appear to be due to switches in RouteAlt. WMATA calculates Dwell_Time by an unknown process. The WMATA calculation is equal to my calculation, except for the records immedaitely before and after a RouteAlt switch (DirChange2).


View(filter(AllDays_NewOrder,
            Dwell_Time != Dwell_Time2
           )
    )


# Examples where the Dwell_Time and Dwell_Time2 are different
View(filter(AllDays_NewOrder,
            ( (RowNum_OG >= 65 & RowNum_OG <= 85) | # 75
                (RowNum_OG >= 162 & RowNum_OG <= 192) | # 172
                (RowNum_OG >= 431952 & RowNum_OG <= 431972) | # 431962
                (RowNum_OG >= 434595 & RowNum_OG <= 434615) # 434605  --  this record is NOT a route switch, but does has a Sequence switch (Me: should there really be a route switch here?)
            )
           )
    )

Investigation of Dwell_Time2 (how long the bus is at a stop).

First, create some “rank” stats. View(DT2_Pctiles): 95% of Dwell_Time2s are <= 23 seconds…but some weird (e.g., nearly 2 hour Dwell_Time2s exist).


DwellTime2_Ntile <- as.data.frame(AllDays_NewOrder$Dwell_Time2) %>% 
  mutate(Pctile = ntile(AllDays_NewOrder$Dwell_Time2, 100),
         MinR = min_rank(AllDays_NewOrder$Dwell_Time2),
         PctR = percent_rank(AllDays_NewOrder$Dwell_Time2),
         PctR_Round = round(PctR, 2)
        ) 

colnames(DwellTime2_Ntile)[1] <- "Dwell_Time2"
str(DwellTime2_Ntile)

DwellTime2_Ntile_Rows <- nrow(DwellTime2_Ntile)

View(tail(DwellTime2_Ntile, 500))


DwellTime2_Pctiles <- group_by(DwellTime2_Ntile,
                               PctR_Round
                              ) %>% 
  summarise(
    MinDwellAtPctile = min(Dwell_Time2),
    CntsAtPctile = n(),
    PctsAtPctile = CntsAtPctile / DwellTime2_Ntile_Rows
  ) %>% 
  mutate(CumSumPAtP = cumsum(PctsAtPctile)
        )

View(DwellTime2_Pctiles)

Investigation of Dwell_Time2 (how long the bus is at a stop).

Histogram of Dwell_Time2.


DwellTime2_HistDen <- ggplot(AllDays_NewOrder, aes(x = Dwell_Time2, y = ..density..)) +
  geom_histogram(binwidth = 1, fill = "lightblue", colour = "grey60", size = 0.2) +
  geom_line(stat = "density", colour = "red") +
  coord_cartesian(xlim = c(1, 25), ylim = c(0, 0.05)
                 ) +
  xlab("Time a Bus Stays at a Stop (sec)") + 
  ylab("Density") + 
  #  theme(legend.position="none") + 
  ggtitle(expression(atop("Variation in How Long a Bus Stays at a Stop"
                          # ,atop(italic("xxxxx"),"")
                         )
                    )
         )

DwellTime2_HistDen

Investigation of Dwell_Time2 (how long the bus is at a stop).

Looking at some weirdly long Dwell_Time2 values.


View(arrange(AllDays_NewOrder,
             desc(Dwell_Time2)
            )
    )


# examples of extremely large Dwell_Time2s
View(filter(AllDays_NewOrder,
            (RowNum_OG >= 292669 & RowNum_OG <= 292689) | # 292679
                (RowNum_OG >= 531057 & RowNum_OG <= 531077) | # 531067
                (RowNum_OG >= 1388627 & RowNum_OG <= 1388647) | # 1388637
                (RowNum_OG >= 1645711 & RowNum_OG <= 1645731) # 1645721
           )
    )


View(filter(AllDays_NewOrder,
            Dwell_Time2 == 0
           )
    )

Investigation of Delta_Time (how early or late the bus is).

View(DT2_Pctiles): 94% of Delta_Time values are between -236 seconds and 1,259 seconds. Roughly 66% of records are within 5 min late and 5 min early…but some weird (e.g., almost 50 minute late or 40 minute early) Delta_Times exist.

Note that Delta_Time is the difference from the scheduled bus arrival. So if two buses are scheduled to arrive at a destination at 10:00pm and 10:20pm, and if the 10:20pm bus has a Delta_Time of 5 minutes, there are 25 minutes between bus arrivals at the stop.

Also note that based on a comment at https://planitmetro.com/2016/11/16/data-download-metrobus-vehicle-location-data/, the Delta_Time values don’t appear to coincide with published bus schedules (e.g., the X2 departing every 8 minutes during peak hours).


DeltTime_Ntile <- as.data.frame(AllDays_NewOrder$Delta_Time) %>% 
  mutate(Pctile = ntile(AllDays_NewOrder$Delta_Time, 100),
         MinR = min_rank(AllDays_NewOrder$Delta_Time),
         PctR = percent_rank(AllDays_NewOrder$Delta_Time),
         PctR_Round = round(PctR, 2)
        ) 

colnames(DeltTime_Ntile)[1] <- "Delta_Time"
str(DeltTime_Ntile)

DeltTime_Ntile_Rows <- nrow(DeltTime_Ntile)

View(tail(DeltTime_Ntile, 500))


DeltTime_Pctiles <- group_by(DeltTime_Ntile,
                             PctR_Round
                            ) %>% 
  summarise(
    MinDeltTimeAtPctile = min(Delta_Time),
    CntsAtPctile = n(),
    PctsAtPctile = CntsAtPctile / DeltTime_Ntile_Rows
  ) %>% 
  mutate(CumSumPAtP = cumsum(PctsAtPctile)
        )

View(DeltTime_Pctiles)
DeltTime_Pctiles

# ~66% of rows are between 5 min late and 5 min early
nrow(filter(AllDays_NewOrder,
            Delta_Time >= -300 &
              Delta_Time <= 300
           )
    ) / nrow(AllDays_NewOrder)


# examples of weird large Delta_Times
View(filter(AllDays_NewOrder,
            Delta_Time < -4202 |
              Delta_Time > 1705
           ) %>% 
       arrange(desc(Delta_Time)
              )
    )

Investigation of Delta_Time (how early or late the bus is).

Delta_Time histogram.


DeltTime_HistDen <- ggplot(AllDays_NewOrder, aes(x = (Delta_Time / 60),
                                                 y = ..density..
                                                )
                          ) +
  geom_histogram(binwidth = (5/60), fill = "lightblue", colour = "grey60", size = 0.2) +
  geom_line(stat = "density", colour = "red") +
  coord_cartesian(xlim = c(-5, 5)) +
  xlab("Bus Lateness (min)") + 
  ylab("Density") + 
  #  theme(legend.position="none") + 
  ggtitle(expression(atop("Variation in How Early/Late a Bus Is",
                          atop(italic("(positive values are late arrivals)"),
                               ""
                              )
                         )
                    )
         )

DeltTime_HistDen

Investigation of Delta_Time (how early or late the bus is).

Delta_Time boxplot.


# Count_Values is needed to display the medians on the box plots
Count_Values <- ddply(AllDays_NewOrder,
                      .(Event_Time_HrGroup),
                      summarise,
                      Value_Counts = median(Delta_Time / 60, na.rm = TRUE)
                     )

DeltTime_BoxPlot <- ggplot(AllDays_NewOrder,
                           aes(factor(Event_Time_HrGroup),
                               Delta_Time / 60,
                               fill = factor(Event_Time_HrGroup)
                              )
                          ) + 
  geom_boxplot(outlier.colour="red", notch=TRUE) + 
  # coord_cartesian(ylim = c(-300, 1200)) +
  coord_cartesian(ylim = c(-5, 20)) +
  geom_text(data = Count_Values,
            aes(y = Value_Counts,
                label = format(round(Value_Counts, digits = 1),
                               nsmall = 1
                              )
               ),
            size = 3,
            vjust = -0.5
           ) +
  xlab("Hour Group") + 
  ylab("Bus Lateness (minutes)") + 
  theme(legend.position="none", axis.text.x = element_text(angle=45)) + 
  #theme(legend.position="right", axis.text.x = element_blank()) + 
  ggtitle(expression(atop("How Early/Late is the Bus (by Hour Group)",
                          atop(italic("(positive values are late arrivals)"),
                               ""
                              )
                         )
                    )
         )

DeltTime_BoxPlot

Investigation of Delta_Time (how early or late the bus is).

Exploring “extreme” Delta_Times. First let’s get some “rank” stats.


View(DeltTime_Pctiles)
DeltTime_Pctiles


DeltTimeAbs_Ntile <- as.data.frame(abs(AllDays_NewOrder$Delta_Time)) %>% 
  mutate(Pctile = ntile(abs(AllDays_NewOrder$Delta_Time), 100),
         MinR = min_rank(abs(AllDays_NewOrder$Delta_Time)),
         PctR = percent_rank(abs(AllDays_NewOrder$Delta_Time)),
         PctR_Round = round(PctR, 2)
        ) 

colnames(DeltTimeAbs_Ntile)[1] <- "Delta_Time_Abs"
str(DeltTimeAbs_Ntile)

DeltTimeAbs_Ntile_Rows <- nrow(DeltTimeAbs_Ntile)

View(tail(DeltTimeAbs_Ntile, 500))


DeltTimeAbs_Pctiles <- group_by(DeltTimeAbs_Ntile,
                                PctR_Round
                               ) %>% 
  summarise(
    MinDeltTimeAtPctile = min(Delta_Time_Abs),
    CntsAtPctile = n(),
    PctsAtPctile = CntsAtPctile / DeltTime_Ntile_Rows
  ) %>% 
  mutate(CumSumPAtP = cumsum(PctsAtPctile)
        )

View(DeltTimeAbs_Pctiles)
DeltTimeAbs_Pctiles

Investigation of Delta_Time (how early or late the bus is).

Exploring “extreme” Delta_Times. Then let’s calculate the percentage of buses that are 10 minutes (or more) late/early.


HrGroup_DeltaTime_All <- group_by(AllDays_NewOrder,
                                  Event_Time_HrGroup
                                 ) %>% 
  summarise(EventAll_Cnt = n()
           )

str(HrGroup_DeltaTime_All)
View(HrGroup_DeltaTime_All)


HrGroup_DeltaTime_Above10Min <- filter(AllDays_NewOrder,
                                       abs(Delta_Time) >= 600
                                      ) %>% 
  group_by(Event_Time_HrGroup) %>% 
  summarise(EventAbove10_Cnt = n()
           )

str(HrGroup_DeltaTime_Above10Min)
View(HrGroup_DeltaTime_Above10Min)


HrGroup_DeltaTimeCompare <- inner_join(HrGroup_DeltaTime_Above10Min,
                                       HrGroup_DeltaTime_All,
                                       by = c("Event_Time_HrGroup" = "Event_Time_HrGroup")
                                      ) %>% 
  mutate(PctEventsAbove10 = EventAbove10_Cnt / EventAll_Cnt)

View(HrGroup_DeltaTimeCompare)

Investigation of Delta_Time (how early or late the bus is).

Quickly plot these “extreme” Delta_Times.


DeltTime_Above10_Cols <- ggplot(HrGroup_DeltaTimeCompare,
                                aes(factor(Event_Time_HrGroup),
                                    PctEventsAbove10
                                   )
                               ) +
  geom_col(fill = "lightblue", colour = "grey60", size = 0.2) +
  geom_text(aes(label = format(round(PctEventsAbove10, digits = 2),
                               nsmall = 2
                              )
               ),
            size = 3,
            nudge_y = (HrGroup_DeltaTimeCompare$PctEventsAbove10 * -0.1)
           ) +
  # coord_cartesian(xlim = c(-5, 5)) +
  xlab("Hour Group") + 
  ylab("Percent of All Bus Arrivals") +
  theme(legend.position="none", axis.text.x = element_text(angle=45)) +
  ggtitle(expression(atop("When is a Bus 10+ Minutes Late/Early"
                          # ,atop(italic("positive values are late arrivals"),
                          #      ""
                          #     )
                         )
                    )
         )

DeltTime_Above10_Cols

Quick investigation on the relationship between Dwell_Time2 (the time a bus is at a stop) and Delta_Time (how early/late the bus is).

Correlation.


DwellTDeltaT_Corr <- as.matrix(cor(x = AllDays_NewOrder$Dwell_Time2,
                                   y = AllDays_NewOrder$Delta_Time,
                                   use = "pairwise"
                                  )
                               )

DwellTDeltaT_Corr

Quick investigation on the relationship between Dwell_Time2 (the time a bus is at a stop) and Delta_Time (how early/late the bus is).

Next, let’s get a sample of data for plotting. Let’s do this for the full dataset (AllDays_NewOrder).


AllDays_NewOrder_10PctSamp <- sample_frac(AllDays_NewOrder, 0.1) %>% 
  select(Delta_Time,
         Dwell_Time2
        ) %>% 
  mutate(DataSet = "AllData")

str(AllDays_NewOrder_10PctSamp)

Quick investigation on the relationship between Dwell_Time2 (the time a bus is at a stop) and Delta_Time (how early/late the bus is).

Let’s also get a sample of data for plotting, but with a datset that removes outliers.


View(DeltTime_Pctiles)
View(DwellTime2_Pctiles)

AllDays_NewOrder_NoExtremes_10PctSamp <- filter(AllDays_NewOrder,
                                                between(Delta_Time, -402, 1705) & # removes about 2% of Delta_Time values
                                                  between(Dwell_Time2, 1, 63)  # removes about 2% of Dwell_Time2 values
                                               ) %>% 
  sample_frac(0.1) %>% 
  select(Delta_Time,
         Dwell_Time2
        ) %>% 
  mutate(DataSet = "OutliersRemoved")

str(AllDays_NewOrder_NoExtremes_10PctSamp)

Quick investigation on the relationship between Dwell_Time2 (the time a bus is at a stop) and Delta_Time (how early/late the bus is).

Plotting the data from the dataset that does not remove outliers.


DwellTDeltaT_Scatter <- ggplot(AllDays_NewOrder_10PctSamp,
                               aes(Dwell_Time2, Delta_Time)
                              ) +
  geom_point(shape = 1, alpha = 0.5) +
  scale_shape(solid = FALSE) +
  geom_smooth(method = "lm", colour = "red") +
  # xlab("Time at Stop (sec)") + 
  # ylab("Lateness (sec)") +
  annotate(label = lm_eqn(df = AllDays_NewOrder_10PctSamp,
                          y = AllDays_NewOrder_10PctSamp$Delta_Time,
                          x = AllDays_NewOrder_10PctSamp$Dwell_Time2
                         ),
           x = 2200,
           y = 600,
           geom = "text",
           size = 3,
           colour = "red",
           parse = TRUE
          ) +
  labs(title = "Lateness vs Time at Stop",
       subtitle = "(no outliers removed)",
       x = "Time at Stop (sec)",
       y = "Lateness (sec)"
      )
  # ggtitle(expression(atop("Lateness vs Time at Stop"
  #                         ,atop(italic("(no outliers removed)"),
  #                               ""
  #                              )
  #                        )
  #                   )
  #        )
# +
#   geom_jitter()

DwellTDeltaT_Scatter

Quick investigation on the relationship between Dwell_Time2 (the time a bus is at a stop) and Delta_Time (how early/late the bus is).

Plotting the data from the dataset that does remove outliers.


DwellTDeltaT_Scatter_NoExtremes <- ggplot(AllDays_NewOrder_NoExtremes_10PctSamp,
                                          aes(Dwell_Time2, Delta_Time)
                                         ) +
  geom_point(shape = 1, alpha = 0.5) +
  scale_shape(solid = FALSE) +
  geom_smooth(method = "lm", colour = "blue") +
  # xlab("Time at Stop (sec)") + 
  # ylab("Lateness (sec)") +
  annotate(label = lm_eqn(df = AllDays_NewOrder_NoExtremes_10PctSamp,
                          y = AllDays_NewOrder_NoExtremes_10PctSamp$Delta_Time,
                          x = AllDays_NewOrder_NoExtremes_10PctSamp$Dwell_Time2
                         ),
           x = 50,
           y = -475,
           geom = "text",
           size = 3,
           colour = "blue",
           parse = TRUE
          ) +
  labs(title = "Lateness vs Time at Stop",
       subtitle = "(2% of outliers removed)",
       x = "Time at Stop (sec)",
       y = "Lateness (sec)"
      )
  # ggtitle(expression(atop("Lateness vs Time at Stop"
  #                         ,atop(italic("(2% of outliers removed)"),
  #                               ""
  #                              )
  #                        )
  #                   )
  #        )
# +
#   geom_jitter()

DwellTDeltaT_Scatter_NoExtremes

Quick investigation on the relationship between Dwell_Time2 (the time a bus is at a stop) and Delta_Time (how early/late the bus is).

Plotting the data from both datasets together.


CombinedData <- rbind(AllDays_NewOrder_10PctSamp,
                      AllDays_NewOrder_NoExtremes_10PctSamp
                     )

CombinedData$DataSet <- factor(CombinedData$DataSet)

str(CombinedData)


DwellTDeltaT_Scatter_Combined <- ggplot(CombinedData,
                                        aes(x = Dwell_Time2,
                                            y = Delta_Time,
                                            colour = DataSet
                                           )
                                       ) +
  geom_point(shape = 1, alpha = 0.5) +
  scale_shape(solid = FALSE) +
  coord_cartesian(xlim = c(0, 500), ylim = c(-1000, 2000)
                 ) +
  geom_smooth(data = filter(CombinedData,
                            DataSet == "AllData"
                           ),
              method = "lm",
              colour = "red"
             ) +
  geom_smooth(data = filter(CombinedData,
                            DataSet == "OutliersRemoved"
                           ),
              method = "lm",
              colour = "blue"
             ) +
  # facet_wrap( ~ DataSet, ncol = 2) +
  annotate(label = lm_eqn(df = AllDays_NewOrder_10PctSamp,
                          y = AllDays_NewOrder_10PctSamp$Delta_Time,
                          x = AllDays_NewOrder_10PctSamp$Dwell_Time2
                         ),
           x = 300,
           y = -600,
           geom = "text",
           size = 3,
           colour = "red",
           parse = TRUE
          ) +
  annotate(label = lm_eqn(df = AllDays_NewOrder_NoExtremes_10PctSamp,
                          y = AllDays_NewOrder_NoExtremes_10PctSamp$Delta_Time,
                          x = AllDays_NewOrder_NoExtremes_10PctSamp$Dwell_Time2
                         ),
           x = 300,
           y = -800,
           geom = "text",
           size = 3,
           colour = "blue",
           parse = TRUE
          ) +
  theme(legend.position = "bottom") +
  labs(title = "Lateness vs Time at Stop",
       x = "Time at Stop (sec)",
       y = "Lateness (sec)"
      )
  # ggtitle(expression(atop("Lateness vs Time at Stop"
                          # ,atop(italic("2% of outliers removed"),
                          #       ""
                          #      )
         #                 )
         #            )
         # )
# +
#   geom_jitter()

DwellTDeltaT_Scatter_Combined

Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Cmd+Option+I.

When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Cmd+Shift+K to preview the HTML file).

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayBmb3IgV01BVEEgTWV0cm9idXMgRGF0YSIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKICBodG1sX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQoKVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2sgZm9yIGFuYWx5c2lzIHVzaW5nIGRhdGEgb24gdGhlIERDIEJ1cyBTeXN0ZW0gKFdNQVRBIE1ldHJvYnVzKS4gIFRoZSBkYXRhIHdlcmUgb2J0YWluZWQgaGVyZToKCmh0dHBzOi8vcGxhbml0bWV0cm8uY29tLzIwMTYvMTEvMTYvZGF0YS1kb3dubG9hZC1tZXRyb2J1cy12ZWhpY2xlLWxvY2F0aW9uLWRhdGEvCgoKTG9hZCB0aGUgcGFja2FnZXMgdG8gYmUgdXNlZC4KYGBge3IgZWNobyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CgpsaWJyYXJ5KCJqc29ubGl0ZSIpICAgICAgICAgICAjIG1hbmlwdWxhdGluZyBKU09OIGZpbGVzIGZvciB6aXAgY29kZXMKbGlicmFyeSgic3FsZGYiKSAgICAgICAgICAgICAgIyBzcWwtYmFzZWQgZGF0YSBtYW5pcHVsYXRpb24KbGlicmFyeSgidGNsdGsiKQpsaWJyYXJ5KCJ0aWR5ciIpICAgICAgICAgICAgICAjIGRhdGEgbWFuaXB1bGF0aW9uCmxpYnJhcnkoInBseXIiKSAgICAgICAgICAgICAgICMgZGF0YSBtYW5pcHVsYXRpb24KbGlicmFyeSgiZHBseXIiKSAgICAgICAgICAgICAgIyBkYXRhIG1hbmlwdWxhdGlvbgpsaWJyYXJ5KCJtYWdyaXR0ciIpICAgICAgICAgICAjIGRhdGEgbWFuaXB1bGF0aW9uIChwaXBpbmcgZGF0YSkKbGlicmFyeSgic3RyaW5nciIpICAgICAgICAgICAgIyBzdHJpbmcgbWFuaXB1bGF0aW9uCmxpYnJhcnkoImRhdGEudGFibGUiKSAgICAgICAgICMgdXNlZCBpbiB0ZXN0aW5nIGRhdGEgbWFuaXB1bGF0aW9uIGZvciBzcGVlZCBpbmNyZWFzZXMKbGlicmFyeSgibHVicmlkYXRlIikgICAgICAgICAgIyBkYXRlIG1hbmlwdWxhdGlvbgpsaWJyYXJ5KCJnZW9zcGhlcmUiKSAgICAgICAgICAjIGNhbGN1bGF0aW5nIEhhdmVyc2luZSBkaXN0YW5jZQpsaWJyYXJ5KCJnZ3Bsb3QyIikgICAgICAgICAgICAjIGdlbmVyYWwgcGxvdHRpbmcKbGlicmFyeSgiZ2d2aXMiKSAgICAgICAgICAgICAgIyBnZW5lcmFsIHBsb3R0aW5nCmxpYnJhcnkoInJib2tlaCIpICAgICAgICAgICAgICMgZ2VuZXJhbCBwbG90dGluZwpsaWJyYXJ5KCJnZ21hcCIpICAgICAgICAgICAgICAjIGdlbmVyYWwgcGxvdHRpbmcgb2YgbWFwcwpsaWJyYXJ5KCJyZ2RhbCIpICAgICAgICAgICAgICAjIHVzZWQgaW4gcGxvdHRpbmcgc2hhcGVmaWxlcwpsaWJyYXJ5KCJicm9vbSIpICAgICAgICAgICAgICAjIHVzZWQgaW4gcGxvdHRpbmcgc2hhcGVmaWxlcwpsaWJyYXJ5KCJtYXB0b29scyIpICAgICAgICAgICAjIHVzZWQgaW4gcGxvdHRpbmcgc2hhcGVmaWxlcwpsaWJyYXJ5KCJyZ2VvcyIpICAgICAgICAgICAgICAjIHVzZWQgaW4gcGxvdHRpbmcgc2hhcGVmaWxlcwpsaWJyYXJ5KCJjYXJldCIpICAgICAgICAgICAgICAjIHVzZWQgaW4gUENBCmxpYnJhcnkoImNsdXN0ZXIiKSAgICAgICAgICAgICMgdXNlZCBmb3IgY2x1c3RlcmluZwpsaWJyYXJ5KCJmcGMiKSAgICAgICAgICAgICAgICAjIHVzZWQgZm9yIGNsdXN0ZXJpbmcKbGlicmFyeSgiZGJzY2FuIikgICAgICAgICAgICAgIyB1c2VkIGZvciBjbHVzdGVyaW5nCmxpYnJhcnkoIk5iQ2x1c3QiKSAgICAgICAgICAgICMgdXNlZCBmb3IgY2x1c3RlcmluZwpsaWJyYXJ5KCJmYWN0b2V4dHJhIikgICAgICAgICAjIHBsb3R0aW5nIGNsdXN0ZXJzCiMgbGlicmFyeSgiZ3BjbGliIikKIyBpbnN0YWxsLnBhY2thZ2VzKCdyZ2VvcycsIHR5cGU9J3NvdXJjZScpCiMgaW5zdGFsbC5wYWNrYWdlcygncmdkYWwnLCB0eXBlPSdzb3VyY2UnKQojIGluc3RhbGwucGFja2FnZXMoIk5iQ2x1c3QiKQoKYGBgCgoKR2V0IHRoZSBCdXMgZGF0YS4KCkZpcnN0IGxldCdzIGNoZWNrIHRoZSB3b3JraW5nIGRpcmVjdG9yeS4KYGBge3J9CgpnZXR3ZCgpCgpgYGAKCgpUaGVuLCBhY3R1YWxseSBnZXQgdGhlIGRhdGEuCmBgYHtyIGVjaG8gPSBGQUxTRX0KCnNldHdkKCIvVXNlcnMvbWR0dXJzZS9EZXNrdG9wL0FuYWx5dGljcy9EQ01ldHJvQnVzL0J1cyBBVkwgT2N0IDIwMTYiKQoKZm9yIChpIGluIDM6Nyl7CiAgYXNzaWduKHBhc3RlMCgiT2N0MCIsIGksICJSYXciKSwKICAgICAgICAgcmVhZC5kZWxpbShwYXN0ZTAoIjIwMTYxMDAiLCBpLCAiTWV0cm9idXNBVkwudHh0IiksCiAgICAgICAgICAgICAgICAgICAgc2VwID0gIlx0IiwKICAgICAgICAgICAgICAgICAgICBoZWFkZXIgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgIG5hLnN0cmluZ3MgPSBOVUxMCiAgICAgICAgICAgICAgICAgICApCiAgICAgICAgKQogIAogIG1lc3NhZ2UoIk9jdDAiLCBpLCAiUmF3IikKIAogIHN0cihnZXQocGFzdGUwKCJPY3QwIiwgaSwgIlJhdyIpCiAgICAgICAgICkKICAgICApCiAgfQoKYGBgCgoKUHV0IHRoZSBkYWlseSBkYXRhIHRvZ2V0aGVyLgpgYGB7cn0KCkFsbERheXMgPC0gYmluZF9yb3dzKGxpc3QoT2N0MDNSYXcsIE9jdDA0UmF3LCBPY3QwNVJhdywgT2N0MDZSYXcsIE9jdDA3UmF3KSwKICAgICAgICAgICAgICAgICAgICAgLmlkID0gYygiZ3JvdXAiKQogICAgICAgICAgICAgICAgICAgICkKIyBkaW0oQWxsRGF5cykKc3RyKEFsbERheXMpCgpgYGAKCgpEZWxldGluZyBvbGQgZGF0YSBmcmFtZXMuCmBgYHtyfQoKZm9yIChpIGluIDM6Nyl7CiAgcm0obGlzdCA9IGxzKHBhdHRlcm4gPSBwYXN0ZTAoIk9jdDAiLCBpLCAiUmF3IikKICAgICAgICAgICAgICApCiAgICApCiAgCiAgbWVzc2FnZSgiRGVsZXRpbmcgT2N0MCIsIGksICJSYXciKQogIH0KCmBgYAoKClVwZGF0aW5nIHZhcmlhYmxlIHR5cGVzLgoKVGhlbiwgc29ydGluZyB0aGUgZGF0YSBhbmQgYWRkaW5nIGEgUm93TnVtYmVyICh0byBiZSB1c2VkIGZvciBpZGVudGlmeWluZyByb3dzIGxhdGVyIGluIHRoZSBhbmFseXNlcy4pCmBgYHtyfQoKcm0oaSkKCgpBbGxEYXlzJGdyb3VwIDwtIGZhY3RvcihBbGxEYXlzJGdyb3VwKQpBbGxEYXlzJFJvdXRlX0RpcmVjdGlvbiA8LSBmYWN0b3IoQWxsRGF5cyRSb3V0ZV9EaXJlY3Rpb24pCkFsbERheXMkRXZlbnRfVGltZSA8LSBhcy5QT1NJWGN0KEFsbERheXMkRXZlbnRfVGltZSwgZm9ybWF0ID0gIiVtLSVkLSV5ICVJOiVNOiVTICVwIikKQWxsRGF5cyREZXBhcnR1cmVfVGltZSA8LSBhcy5QT1NJWGN0KEFsbERheXMkRGVwYXJ0dXJlX1RpbWUsIGZvcm1hdCA9ICIlbS0lZC0leSAlSTolTTolUyAlcCIpCgpzdHIoQWxsRGF5cykKCgpBbGxEYXlzX1NvcnRlZCA8LSBhcnJhbmdlKEFsbERheXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgQnVzX0lELAogICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWUKICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIG11dGF0ZShSb3dOdW1fT0cgPSByb3dfbnVtYmVyKCkgIyB0aGlzIGlzIHVzZWZ1bCBpbiBpZGVudGlmeSB0aGUgcm93IGxhdGVyIG9uCiAgICAgICAgKQoKcm0oQWxsRGF5cykKc3RyKEFsbERheXNfU29ydGVkKQoKIyBWaWV3KGhlYWQoQWxsRGF5c19Tb3J0ZWQsIDEwMCkpCgpgYGAKCgpJbnNwZWN0aW5nIHRoZSB2YWx1ZXMgb2YgU3RvcF9JRCwgYW5kIGZpbmRpbmcgdGhhdCBpdCBjYW4gdGFrZSB0aGUgdmFsdWVzICIiIChibGFuaykgYW5kICJOVUxMIi4KYGBge3J9CgpWaWV3KGdyb3VwX2J5KEFsbERheXNfU29ydGVkLAogICAgICAgICAgICAgIFN0b3BfSUQKICAgICAgICAgICAgICkgJT4lIAogICAgICAgc3VtbWFyaXNlKAogICAgICAgICBDbnQgPSBuKCkKICAgICAgICAgKSAlPiUgCiAgICAgICBhcnJhbmdlKFN0b3BfSUQpCiAgICApCgpWaWV3KGZpbHRlcihBbGxEYXlzX1NvcnRlZCwKICAgICAgICAgICAgaXMubmEoU3RvcF9JRCkgfAogICAgICAgICAgICAgIFN0b3BfSUQgPT0gIiIgfAogICAgICAgICAgICAgIFN0b3BfSUQgPT0gIk5VTEwiCiAgICAgICAgICAgKSAlPiUgCiAgICAgICBhcnJhbmdlKFN0b3BfRGVzYykKICAgICkKCmBgYAoKCkNyZWF0aW5nIGEgdGFibGUgb2YgZGlzdGluY3QgU3RvcF9EZXNjIHZhbHVlcyB3aGVuIFN0b3BfSUQgaXMgIiIgKGJsYW5rKSBvciAiTlVMTCIuCmBgYHtyfQoKU3RvcElEX05ldyA8LSBmaWx0ZXIoQWxsRGF5c19Tb3J0ZWQsCiAgICAgICAgICAgICAgICAgICAgIGlzLm5hKFN0b3BfSUQpIHwKICAgICAgICAgICAgICAgICAgICAgICBTdG9wX0lEID09ICIiIHwKICAgICAgICAgICAgICAgICAgICAgICBTdG9wX0lEID09ICJOVUxMIgogICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHNlbGVjdChTdG9wX0lELCBTdG9wX0Rlc2MpICU+JSAKICBkaXN0aW5jdCgpICU+JSAKICBhcnJhbmdlKFN0b3BfSUQsIFN0b3BfRGVzYykgJT4lIAogIG11dGF0ZShTdG9wSURfTmV3ID0gMTpucm93KC4pCiAgICAgICAgKQoKVmlldyhTdG9wSURfTmV3KQpTdG9wSURfTmV3CgpgYGAKCgpDcmVhdGluZyBhIGZ1bGwgdXBkYXRlZCB0YWJsZSBieSBmaWxsaW5nIGluIFN0b3BJRF9OZXcgZm9yIHdoZW4gU3RvcF9JRCBpcyAiIiAoYmxhbmspIG9yIE5VTEwuCmBgYHtyfQoKQWxsRGF5c19TdG9wSUROZXcgPC0gbGVmdF9qb2luKEFsbERheXNfU29ydGVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KFN0b3BJRF9OZXcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RvcF9EZXNjLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0b3BJRF9OZXcKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IGMoIlN0b3BfRGVzYyIgPSAiU3RvcF9EZXNjIikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKFN0b3BJRF9DbGVhbiA9IGlmZWxzZShpcy5uYShTdG9wSURfTmV3KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0b3BfSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdG9wSURfTmV3CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFN0b3BJRF9JbmRpY2F0b3IgPSBmYWN0b3IoaWZlbHNlKGlzLm5hKFN0b3BJRF9OZXcpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSURfT0siLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSURfQmFkIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICApCgpybShTdG9wSURfTmV3KQpybShBbGxEYXlzX1NvcnRlZCkKc3RyKEFsbERheXNfU3RvcElETmV3KQoKIyBWaWV3KHRhaWwoQWxsRGF5c19TdG9wSUROZXcsIDUwMCkpCiMgVmlldyhmaWx0ZXIoQWxsRGF5c19TdG9wSUROZXcsCiMgICAgICAgICAgICAgU3RvcF9EZXNjID09ICJNRVRST1dBWSBBTk5OT1VDRU1OVCBDT1JSIgojICAgICAgICAgICAgKQojICAgICApCgpgYGAKCgpMYXQgTG9uZyBzdGF0cyBmb3IgcHVsbGluZyBpbiBaaXAgY29kZXMgbGF0ZXIuCmBgYHtyfQoKTExfU3RhdHMgPC0gZ3JvdXBfYnkoQWxsRGF5c19TdG9wSUROZXcsCiAgICAgICAgICAgICAgICAgICAgIFN0b3BJRF9DbGVhbgogICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHN1bW1hcmlzZShMYXRfTWVhbiA9IG1lYW4oTGF0aXR1ZGUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIExhdF9NZWQgPSBtZWRpYW4oTGF0aXR1ZGUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIExuZ19NZWFuID0gbWVhbihMb25naXR1ZGUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIExuZ19NZWQgPSBtZWRpYW4oTG9uZ2l0dWRlLCBuYS5ybSA9IFRSVUUpCiAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKExhdF9NZWFMZXNzTWVkID0gTGF0X01lYW4gLSBMYXRfTWVkLAogICAgICAgICBMbmdfTWVhTGVzc01lZCA9IExuZ19NZWFuIC0gTG5nX01lZCwKICAgICAgICAgUm93TnVtID0gcm93X251bWJlcigpCiAgICAgICAgKQoKc3RyKExMX1N0YXRzKQpzdW1tYXJ5KExMX1N0YXRzKQoKVmlldyhoZWFkKGFycmFuZ2UoTExfU3RhdHMsCiAgICAgICAgICAgICAgICAgIExhdF9NZWFMZXNzTWVkCiAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgIDUwMAogICAgICAgICApCiAgICApCgpWaWV3KGhlYWQoYXJyYW5nZShMTF9TdGF0cywKICAgICAgICAgICAgICAgICAgZGVzYyhMYXRfTWVhTGVzc01lZCkKICAgICAgICAgICAgICAgICApLAogICAgICAgICAgNTAwCiAgICAgICAgICkKICAgICkKClZpZXcoaGVhZChhcnJhbmdlKExMX1N0YXRzLAogICAgICAgICAgICAgICAgICBMbmdfTWVhTGVzc01lZAogICAgICAgICAgICAgICAgICksCiAgICAgICAgICA1MDAKICAgICAgICAgKQogICAgKQoKVmlldyhoZWFkKGFycmFuZ2UoTExfU3RhdHMsCiAgICAgICAgICAgICAgICAgIGRlc2MoTG5nX01lYUxlc3NNZWQpCiAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgIDUwMAogICAgICAgICApCiAgICApCgpgYGAKCgpQdWxsaW5nIGluIFppcCBDb2RlIGRhdGEgZnJvbSBhcGkuZ2VvbmFtZXMub3JnLgpgYGB7cn0KCiMgVVJMIEVYQU1QTEU6CiMgaHR0cDovL2FwaS5nZW9uYW1lcy5vcmcvZmluZE5lYXJieVBvc3RhbENvZGVzSlNPTj9sYXQ9MzguODk1NjAmbG5nPS03Ni45NDg3MyZyYWRpdXM9MCZ1c2VybmFtZT1zdXBlcm1kYXQKCnVybF8xIDwtICJodHRwOi8vYXBpLmdlb25hbWVzLm9yZy9maW5kTmVhcmJ5UG9zdGFsQ29kZXNKU09OP2xhdD0iCnVybF8yIDwtICImbG5nPSIKdXJsXzMgPC0gIiZyYWRpdXM9MCZ1c2VybmFtZT0iCnVzZXJuYW1lIDwtICJzdXBlcm1kYXQiCgoKIyBuZWVkIHRvIGdyb3VwIGluIGJ1bmNoZXMgYXMgaHR0cDovL2FwaS5nZW9uYW1lcy5vcmcgbGltaXRzIHB1bGxzIHRvIDIwMDAgcGVyIGhvdXIKCgojIyMjIyBTdG9yZSBldmVyeXRoaW5nIGluIG11bHRpcGxlIGxpc3RzCnBhZ2VzMSA8LSBsaXN0KCkKCgpzeXN0ZW0udGltZSgKCmZvcihpIGluIDE6MTAwMCl7CiAgbGF0IDwtIGZpbHRlcihMTF9TdGF0cywKICAgICAgICAgICAgICAgIFJvd051bSA9PSBpCiAgICAgICAgICAgICAgICkgJT4lCiAgICBzZWxlY3QoTGF0X01lZCkKICAKICBsbmcgPC0gZmlsdGVyKExMX1N0YXRzLAogICAgICAgICAgICAgICAgUm93TnVtID09IGkKICAgICAgICAgICAgICAgKSAlPiUKICAgIHNlbGVjdChMbmdfTWVkKQogIAogIEFQSURhdGExIDwtIGZyb21KU09OKHBhc3RlMCh1cmxfMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cmxfMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbG5nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB1cmxfMywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXNlcm5hbWUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgIGZsYXR0ZW4gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICApCiAgCiAgbWVzc2FnZSgiUmV0cmlldmluZyBaaXAgQ29kZSAiLCBpKQogIAogIHBhZ2VzMVtbaV1dIDwtIEFQSURhdGExJHBvc3RhbENvZGVzCiAgCn0KKQoKCiMjIyMjIENvbWJpbmUgdGhlIGxpc3RzIGludG8gb25lIHBhZ2UKWmlwczEgPC0gcmJpbmQucGFnZXMocGFnZXMxW3NhcHBseShwYWdlczEsIGxlbmd0aCkgPiAwXSkKCgojIyMjIyBDb21iaW5lIGFsbCBwYWdlcwpaaXBzX0FsbCA8LSBiaW5kX3Jvd3MoWmlwczAsCiAgICAgICAgICAgICAgICAgICAgICBaaXBzMSwKICAgICAgICAgICAgICAgICAgICAgIFppcHMyLAogICAgICAgICAgICAgICAgICAgICAgWmlwczMsCiAgICAgICAgICAgICAgICAgICAgICBaaXBzNCwKICAgICAgICAgICAgICAgICAgICAgIFppcHM1LAogICAgICAgICAgICAgICAgICAgICAgWmlwczYsCiAgICAgICAgICAgICAgICAgICAgICBaaXBzNywKICAgICAgICAgICAgICAgICAgICAgIFppcHM4LAogICAgICAgICAgICAgICAgICAgICAgWmlwczksCiAgICAgICAgICAgICAgICAgICAgICBaaXBzMTAsCiAgICAgICAgICAgICAgICAgICAgICAuaWQgPSAiaWQiCiAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIG11dGF0ZShVbmlxdWVMYXRMbmcgPSBwYXN0ZShsYXQsIGxuZywgc2VwID0gIl9fIikKICAgICAgICApCgojIHN0cihaaXBzX0FsbCkKIyBWaWV3KGhlYWQoWmlwc19BbGwpKQoKCiMgc3RyKExMX1N0YXRzKQpMTF9TdGF0c19VbnFMYXRMbmcgPC0gbXV0YXRlKExMX1N0YXRzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFVuaXF1ZUxhdExuZyA9IHBhc3RlKExhdF9NZWQsIExuZ19NZWQsIHNlcCA9ICJfXyIpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICApCgojIHN0cihMTF9TdGF0c19VbnFMYXRMbmcpCiMgVmlldyhoZWFkKExMX1N0YXRzX1VucUxhdExuZykpCgoKTExfU3RhdHNaaXBzIDwtIGxlZnRfam9pbihMTF9TdGF0c19VbnFMYXRMbmcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgWmlwc19BbGwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSBjKCJVbmlxdWVMYXRMbmciID0gIlVuaXF1ZUxhdExuZyIpCiAgICAgICAgICAgICAgICAgICAgICAgICApCgpzdHIoTExfU3RhdHNaaXBzKQojIFZpZXcoaGVhZChMTF9TdGF0c1ppcHMpKQoKIyBOb3Qgc3VyZSB3aGV5IHRoZXNlIGNvdWxkbid0IGJlIGZvdW5kICh3aHkgdGhleSdyZSBOQSkKVmlldyhmaWx0ZXIoTExfU3RhdHNaaXBzLAogICAgICAgICAgICBpcy5uYShwb3N0YWxDb2RlKQogICAgICAgICAgICkKICAgICkKCmBgYAoKCkpvaW4gdG8gY3JlYXRlIG9uZSBkYXRhc2V0IHRoYXQgYWxzbyBpbmNsdWRlcyBaaXAgdmFyaWFibGVzLgpgYGB7cn0KCnJtKHVybF8xLCB1cmxfMiwgdXJsXzMsIHVzZXJuYW1lLCBwYWdlczAsIHBhZ2VzMSwgcGFnZXMyLCBwYWdlczMsIHBhZ2VzNCwgcGFnZXM1LCBwYWdlczYsIHBhZ2VzNywgcGFnZXM4LCBwYWdlczksIHBhZ2VzMTAsIGksIGxhdCwgbG5nLCBBUElEYXRhMCwgQVBJRGF0YTEsIEFQSURhdGEyLCBBUElEYXRhMywgQVBJRGF0YTQsIEFQSURhdGE1LCBBUElEYXRhNiwgQVBJRGF0YTcsIEFQSURhdGE4LCBBUElEYXRhOSwgQVBJRGF0YTEwLCBMTF9TdGF0cywgTExfU3RhdHNfVW5xTGF0TG5nKQoKCkFsbERheXNfWmlwcyA8LSBsZWZ0X2pvaW4oQWxsRGF5c19TdG9wSUROZXcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgTExfU3RhdHNaaXBzLAogICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gYygiU3RvcElEX0NsZWFuIiA9ICJTdG9wSURfQ2xlYW4iKQogICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgcmVuYW1lKFN0b3BfU3RhdGUgPSBhZG1pbkNvZGUxLAogICAgICAgICBTdG9wX0NvdW50eSA9IGFkbWluTmFtZTIsCiAgICAgICAgIFN0b3BfQ2l0eSA9IHBsYWNlTmFtZSwKICAgICAgICAgU3RvcF9aaXAgPSBwb3N0YWxDb2RlCiAgICAgICAgICkKCnJtKEFsbERheXNfU3RvcElETmV3LCBMTF9TdGF0c1ppcHMpCnN0cihBbGxEYXlzX1ppcHMpCgpgYGAKCgpVcGRhdGluZyB2YXJpYWJsZSB0eXBlcy4KYGBge3J9CgpBbGxEYXlzX1ppcHMkU3RvcF9TdGF0ZSA8LSBmYWN0b3IoQWxsRGF5c19aaXBzJFN0b3BfU3RhdGUpCkFsbERheXNfWmlwcyRTdG9wX0NvdW50eSA8LSBmYWN0b3IoQWxsRGF5c19aaXBzJFN0b3BfQ291bnR5KQpBbGxEYXlzX1ppcHMkU3RvcF9aaXAgPC0gZmFjdG9yKEFsbERheXNfWmlwcyRTdG9wX1ppcCkKQWxsRGF5c19aaXBzJFN0b3BfQ2l0eSA8LSBmYWN0b3IoQWxsRGF5c19aaXBzJFN0b3BfQ2l0eSkKCkFsbERheXNfWmlwcyRkaXN0YW5jZSA8LSBhcy5udW1lcmljKEFsbERheXNfWmlwcyRkaXN0YW5jZSkKQWxsRGF5c19aaXBzJGNvdW50cnlDb2RlIDwtIGZhY3RvcihBbGxEYXlzX1ppcHMkY291bnRyeUNvZGUpCkFsbERheXNfWmlwcyRhZG1pbk5hbWUxIDwtIGZhY3RvcihBbGxEYXlzX1ppcHMkYWRtaW5OYW1lMSkKCnN0cihBbGxEYXlzX1ppcHMpCgpgYGAKCgpGZWF0dXJlIGVuZ2luZWVyaW5nLgoKSW5zcGVjdGluZyBpbmNpZGVuY2VzIG9mIGNvbnNlY3V0aXZlIFN0b3BfSURzLiBUaGlzIGlzIGRvbmUgYmVjYXVzZSBpbnZlc3RpZ2F0aW9uIHNob3dlZCB0aGF0IG1hbnkgY29uc2V1dGl2ZSBldmVudHMgb2NjdXJyIGF0IHRoZSBzYW1lIFN0b3BfSUQsIGJ1dCB3aXRoIHZhcmlvdXMgRHdlbGxfVGltZXMsIE9kb21ldGVyX0Rpc3RhbmNlcywgZXRjLiAgQWxsIG9mIHdoaWNoIGFmZmVjdCBjYWxjdWxhdGlvbnMgYW5kIGFuYWx5c2VzLgoKQ3JlYXRlIGRhdGEgb24gdGhlIHJ1bnMgKGNvbnNlY3V0aXZlIFN0b3BfSURzKS4KYGBge3J9CgpTdG9wSURfUnVucyA8LSBybGUoQWxsRGF5c19aaXBzJFN0b3BJRF9DbGVhbikKClN0b3BJRF9SdW5zJGVuZHMgPC0gY3Vtc3VtKFN0b3BJRF9SdW5zJGxlbmd0aHMpCgpTdG9wSURfUnVucyRzdGFydHMgPC0gaWZlbHNlKGlzLm5hKGxhZyhTdG9wSURfUnVucyRlbmRzKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhZyhTdG9wSURfUnVucyRlbmRzKSArIDEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCnN0cihTdG9wSURfUnVucykKIyBjbGFzcyhTdG9wSURfUnVucykKIyAKIyBTdG9wSURfUnVuc19kZiA8LSBkYXRhLmZyYW1lKHVuY2xhc3MoU3RvcElEX1J1bnMpKQojIHN0cihTdG9wSURfUnVuc19kZikKIyBjbGFzcyhTdG9wSURfUnVuc19kZikKIyBybShTdG9wSURfUnVuc19kZikKCmBgYAoKClRyeWluZyB0byBsaW5rIGRhdGEgb24gUnVuc0dyb3VwcyB3aXRoIHRoZSBvcmlnaW5hbCBkYXRhIChBbGxEYXlzX1NvcnRlZCkuIFRoZSBnb2FsIGlzIHRvIHNlbGVjdCBvbmx5IG9uZSByZWNvcmQgcGVyIFJ1bnNHcm91cCAtIHRoYXQgYmVpbmcgdGhlIHJlY29yZCB3aXRoIHRoZSBsb25nZXN0IER3ZWxsX1RpbWUuCgpJIGF0dGVtcHRlZCB0aGlzIGNvbXB1dGF0aW9uIHVzaW5nIGJvdGggZGF0YS5mcmFtZXMgKGRwbHlyKSBhbmQgZGF0YS50YWJsZXMgKGRhdGEudGFibGUpLiBIb3dldmVyLCB3aXRoIDIsODA5LDA2MiByb3dzIGluIG9uZSBkYXRhc2V0IGFuZCAzLDExOSw0NDMgcm93cyBpbiB0aGUgb3RoZXIgZGF0YXNldCwgdGhlIGN1cnJlbnQgY29tcHV0YXRpb24gdGltZSBpcyBvdmVyIDUgZGF5cy4uLnNvIEknbSB0cnlpbmcgYSBkaWZmZXJlbnQgc3RyYXRlZ3kgdG8gb25seSBzZWxlY3QgdGhlIGZpcnN0IHJlY29yZCBpbiBhIHJ1bi4KYGBge3J9CgojIENyZWF0ZSBhIFJ1bnNHcm91cCB2YXJpYWJsZSBmb3IgZWFjaCBydW4KIyBTdG9wSURfUnVuc19kZiRSdW5zR3JvdXAgPC0gcGFzdGUwKCJnIiwgc2VxKDE6bnJvdyhTdG9wSURfUnVuc19kZikKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiMgCiMgc3RyKFN0b3BJRF9SdW5zX2RmKQojIGhlYWQoU3RvcElEX1J1bnNfZGYsIDI1KQojIHRhaWwoU3RvcElEX1J1bnNfZGYsIDI1KQojIAojIFN0b3BJRF9SdW5zX2RmIDwtIFN0b3BJRF9SdW5zX2RmICU+JSAKIyAgIG11dGF0ZShSb3dOdW0gPSByb3dfbnVtYmVyKCkKIyAgICAgICAgICkKIyAKIyBzdHIoU3RvcElEX1J1bnNfZGYpCiMgaGVhZChTdG9wSURfUnVuc19kZiwgMjUpCiMgdGFpbChTdG9wSURfUnVuc19kZiwgMjUpCiMgCiMgCiMgIyBDb252ZXJ0aW5nIHRvIGRhdGEudGFibGVzIGZvciwgaG9wZWZ1bGx5LCBpbXByb3ZlZCBwZXJmb3JtYW5jZSAoc3BlZWQpIGluIGNvbXB1dGF0aW9uCiMgU3RvcElEX1J1bnNfZHQgPC0gZGF0YS50YWJsZShTdG9wSURfUnVuc19kZikKIyBzZXRrZXkoU3RvcElEX1J1bnNfZHQsIFJvd051bSkKIyBzdHIoU3RvcElEX1J1bnNfZHQpCiMgCiMgQWxsRGF5c19Tb3J0ZWRfZHQgPC0gZGF0YS50YWJsZShBbGxEYXlzX1NvcnRlZCkKIyBzZXRrZXkoQWxsRGF5c19Tb3J0ZWRfZHQsIFJvd051bV9PRykKIyBzdHIoQWxsRGF5c19Tb3J0ZWRfZHQpCiMgIyBybShBbGxEYXlzX1NvcnRlZF9kdCkKIyAKIyAKIyAjIEFjdHVhbCBsb29wIHRvIHBlcmZvcm0gdGhlIGNvbXB1dGF0aW9ucyBhbmQgbGluayB0byBvcmlnaW5hbCBkYXRhIChBbGxEYXlzX1NvcnRlZF9kdCkKIyBHcm91cERhdGEgPC0gbGlzdCgpCiMgZm9yKGkgaW4gMTpucm93KFN0b3BJRF9SdW5zX2R0KQojICAgICkgewojICAgYXNzaWduKHBhc3RlMCgiZ3JvdXBfIiwgaSksCiMgICAgICAgICAgICBTdG9wSURfUnVuc19kdFtSb3dOdW0gPT0gaSwgUnVuc0dyb3VwXQojICAgICAgICAgICApCiMgCiMgICAgICMjIyMjICBUaGUgY29kZSBiZWxvdyBpcyB0aGUgc2FtZSBjb2RlIGFzIGFib3ZlLCBidXQgZG9uZSB3aXRoIGRwbHlyICAjIyMjIwojIAojICAgICAjIGFzc2lnbihwYXN0ZTAoImdyb3VwXyIsIGkpLAojICAgIyAgICAgICAgZmlsdGVyKFN0b3BJRF9SdW5zX2RmLAojICAgIyAgICAgICAgICAgICAgIFJvd051bSA9PSBpCiMgICAjICAgICAgICAgICAgICApICU+JSAKIyAgICMgICAgICAgICAgc2VsZWN0KFJ1bnNHcm91cCkKIyAgICMgICAgICAgKQojIAojICAgYXNzaWduKHBhc3RlMCgiZ3JvdXBfIiwgaSwgIl9zdGFydCIpLAojICAgICAgICAgIFN0b3BJRF9SdW5zX2R0W1Jvd051bSA9PSBpLCBzdGFydHNdCiMgICAgICAgICApCiMgCiMgICBhc3NpZ24ocGFzdGUwKCJncm91cF8iLCBpLCAiX2VuZCIpLAojICAgICAgICAgIFN0b3BJRF9SdW5zX2R0W1Jvd051bSA9PSBpLCBlbmRzXQojICAgICAgICAgKQojIAojICAgYXNzaWduKHBhc3RlMCgiZ3JvdXBfIiwgaSwgIl9yb3dzIiksCiMgICAgICAgICAgQWxsRGF5c19Tb3J0ZWRfZHRbUm93TnVtX09HID49IGFzLm51bWVyaWMoZ2V0KHBhc3RlMCgiZ3JvdXBfIiwgaSwgIl9zdGFydCIpCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAmCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgUm93TnVtX09HIDw9IGFzLm51bWVyaWMoZ2V0KHBhc3RlMCgiZ3JvdXBfIiwgaSwgIl9lbmQiKQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgUnVuc0dyb3VwIDo9IGFzLmNoYXJhY3RlcihnZXQocGFzdGUwKCJncm91cF8iLCBpKQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiMgICAgICAgICAgICAgICAgICAgICAgICAgICBdCiMgCiMgICAgICMjIyMjICBUaGUgY29kZSBiZWxvdyBpcyB0aGUgc2FtZSBhcyB0aGUgY29kZSBhYm92ZSwgYnV0IGRvbmUgd2l0aCBkcGx5ciAgIyMjIyMKIyAKIyAgICAgICAgICAjIGZpbHRlcihBbGxEYXlzX1NvcnRlZCwKIyAgICAgICAgICAjICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywKIyAgICAgICAgICAjICAgICAgICAgICAgICAgIGFzLm51bWVyaWMoZ2V0KHBhc3RlMCgiZ3JvdXBfIiwgaSwgIl9zdGFydCIpCiMgICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKIyAgICAgICAgICAjICAgICAgICAgICAgICAgICAgICAgICAgICApLAojICAgICAgICAgICMgICAgICAgICAgICAgICAgYXMubnVtZXJpYyhnZXQocGFzdGUwKCJncm91cF8iLCBpLCAiX2VuZCIpCiMgICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKIyAgICAgICAgICAjICAgICAgICAgICAgICAgICAgICAgICAgICApCiMgICAgICAgICAgIyAgICAgICAgICAgICAgICkKIyAgICAgICAgICAjICAgICAgICkgJT4lIAojICAgICAgICAgICMgICBtdXRhdGUoUnVuc0dyb3VwID0gYXMuY2hhcmFjdGVyKGdldChwYXN0ZTAoImdyb3VwXyIsIGkpCiMgICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiMgICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKIyAgICAgICAgICAjICAgICAgICApCiMgICAgICAgICApCiMgCiMgICBHcm91cERhdGFbW2ldXSA8LSBnZXQocGFzdGUwKCJncm91cF8iLCBpLCAiX3Jvd3MiKSkKIyAKIyAgIG1lc3NhZ2UoIlByb2Nlc3NpbmcgR3JvdXAgIiwgaSwgIiBvZiAyLDgwOSwwNjIiKQojIH0KIyAKIyAKIyBHcm91cERhdGFfZGYgPC0gcmJpbmQuZmlsbChHcm91cERhdGEpCiMgc3RyKEdyb3VwRGF0YV9kZikKIyBoZWFkKEdyb3VwRGF0YV9kZikKIyB0YWlsKEdyb3VwRGF0YV9kZikKIyAjIHJtKEdyb3VwRGF0YV9kZikKIyAKIyAKIyBncm91cF8xCiMgZ3JvdXBfMV9zdGFydAojIGdyb3VwXzFfZW5kCiMgZ3JvdXBfMV9yb3dzCiMgZ3JvdXBfMl9yb3dzCiMgZ3JvdXBfM19yb3dzCiMgZ3JvdXBfNTBfcm93cwojIHN0cihncm91cF81MF9yb3dzKQojIGdyb3VwXzI4MDkwNjJfcm93cwojIEdyb3VwRGF0YVtbMV1dCiMgR3JvdXBEYXRhW1s1MF1dCiMgCiMgCiMgIyMjIyMgIFRlc3RpbmcgQXJlYSAoQmVsb3cpICAjIyMjIwojICMjIyMjICBUZXN0aW5nIEFyZWEgKEJlbG93KSAgIyMjIyMKIyAjIyMjIyAgVGVzdGluZyBBcmVhIChCZWxvdykgICMjIyMjCiMgCiMgIyBoZWFkKFN0b3BJRF9SdW5zJHN0YXJ0cywgMjApCiMgIyBoZWFkKEFsbERheXNfTmV3T3JkZXIkU3RvcF9JRCwgMjApCiMgIyAKIyAjIAojICMgZGF0IDwtIGFzLmRhdGEuZnJhbWUoYygxLDEsNyw3LDcsOSw2LDgsMiwyLDIsMSwxLDEsMSwxKSkKIyAjIGNvbG5hbWVzKGRhdClbMV0gPC0gImRhdCIKIyAjIHIgPC0gcmxlKGRhdCRkYXQpCiMgIyBkYXQkcnVuIDwtIHJlcChyJGxlbmd0aHMsIHIkbGVuZ3RocykKIyAjIGRhdCRydW5MYWcgPC0gbGFnKGRhdCRydW4pCiMgIyBkYXQkY29uZCA8LSByZXAociR2YWx1ZXMsIHIkbGVuZ3RocykKIyAjIGRhdAojICMgVmlldyhkYXQpCgpgYGAKCgpXaGVuIGNvbnNlY3V0aXZlIFN0b3BfSUQgb2NjdXJycywgb25seSB0YWtlIHRoZSBmaXJzdCBvY2N1cnJlbmNlLiBUaGlzIGlzIGRvbmUgYmVjYXVzZSB0aGUgY29tcHV0YXRpb24gdGltZSB0byBzZWxlY3Qgb25seSB0aGUgcmVjb3JkIHdpdGggdGhlIGxvbmdlc3QgRHdlbGxfVGltZSBmb3IgZWFjaCBydW4gd2FzIHRvbyBsb25nIChvdmVyIDUgZGF5cykuCgpUaGlzIGlzIHByb2JhYmx5IGxlc3MgdGhhbiBpZGVhbCB3aXRoIHJlZ2FyZHMgdG8gRHdlbGxfVGltZSwgYnV0IHNob3VsZCBub3QgbWFrZSBtdWNoIGRpZmZlcmVuY2UgZm9yIGNhbGN1bGF0aW9ucyBvZiB0cmF2ZWwgdGltZSwgc3BlZWQsIGV0Yy4KYGBge3J9CgpBbGxEYXlzX0ZpcnN0U3RvcElEIDwtIEFsbERheXNfWmlwc1tTdG9wSURfUnVucyRzdGFydHMsIF0KCmRpbShBbGxEYXlzX1ppcHMpCmRpbShBbGxEYXlzX0ZpcnN0U3RvcElEKQoKbnJvdyhBbGxEYXlzX1ppcHMpIC0gbnJvdyhBbGxEYXlzX0ZpcnN0U3RvcElEKQoKcm0oQWxsRGF5c19aaXBzLCBTdG9wSURfUnVucykKc3RyKEFsbERheXNfRmlyc3RTdG9wSUQpCgpgYGAKCgpGZWF0dXJlIGVuZ2luZWVyaW5nLgoKQ3JlYXRpbmcgbmV3IHZhcmlhYmxlcy4KYGBge3J9CgpBbGxEYXlzX0FkZFZhcnMgPC0gbXV0YXRlKEFsbERheXNfRmlyc3RTdG9wSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgT2RvbWV0ZXJfRGlzdGFuY2VfTWkgPSBPZG9tZXRlcl9EaXN0YW5jZSAvIDUyODAsICM1LDI4MCBmZWV0IGluIDEgbWlsZQogICAgICAgICAgICAgICAgICAgICAgICAgIER3ZWxsX1RpbWUyID0gYXMubnVtZXJpYyhEZXBhcnR1cmVfVGltZSAtIEV2ZW50X1RpbWUpLAogICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfWXIgPSBhcy5pbnRlZ2VyKHllYXIoRXZlbnRfVGltZSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfTXRoID0gYXMuaW50ZWdlcihtb250aChFdmVudF9UaW1lKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgRXZlbnRfVGltZV9EYXRlID0gZGF5KEV2ZW50X1RpbWUpLAogICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfRGF5ID0gd2RheShFdmVudF9UaW1lLCBsYWJlbCA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfSHIgPSBob3VyKEV2ZW50X1RpbWUpLAogICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfTWluID0gbWludXRlKEV2ZW50X1RpbWUpLAogICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfSHJHcm91cCA9IGZhY3RvcihpZmVsc2UoRXZlbnRfVGltZV9IciA8IDMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR3JvdXAwXzIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoRXZlbnRfVGltZV9IciA8IDYsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR3JvdXAzXzUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoRXZlbnRfVGltZV9IciA8IDksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR3JvdXA2XzgiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoRXZlbnRfVGltZV9IciA8IDEyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdyb3VwOV8xMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShFdmVudF9UaW1lX0hyIDwgMTUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR3JvdXAxMl8xNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShFdmVudF9UaW1lX0hyIDwgMTgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR3JvdXAxNV8xNyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShFdmVudF9UaW1lX0hyIDwgMjEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR3JvdXAxOF8yMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShFdmVudF9UaW1lX0hyIDwgMjQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR3JvdXAyMV8yMyIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSkpKSkpKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGMoIkdyb3VwMF8yIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR3JvdXAzXzUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHcm91cDZfOCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdyb3VwOV8xMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdyb3VwMTJfMTQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJHcm91cDE1XzE3IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiR3JvdXAxOF8yMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkdyb3VwMjFfMjMiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlcmVkID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICkKCnJtKEFsbERheXNfRmlyc3RTdG9wSUQpCnN0cihBbGxEYXlzX0FkZFZhcnMpCgpgYGAKCgpGdW5jdGlvbiBmb3IgY2FsY3VsYXRpbmcgdGhlIGRpc3RhbmNlIHRyYXZlbGVkIGJhc2VkIG9uIHRoZSBIYXZlcnNpbmUgZm9ybXVsYS4gIE9yaWdpbmFsIGNvZGUgZnJvbTogaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vZ3JlYXQtY2lyY2xlLWRpc3RhbmNlLWNhbGN1bGF0aW9ucy1pbi1yLwpgYGB7cn0KCiMgQ2FsY3VsYXRlcyB0aGUgZ2VvZGVzaWMgZGlzdGFuY2UgYmV0d2VlbiB0d28gcG9pbnRzIHNwZWNpZmllZCBieSByYWRpYW4gbGF0aXR1ZGUvbG9uZ2l0dWRlIHVzaW5nIHRoZSBIYXZlcnNpbmUgZm9ybXVsYSAoaGYpCiMgZ2NkLmhmIDwtIGZ1bmN0aW9uKGxvbmcxLCBsYXQxLCBsb25nMiwgbGF0MikgewojICAgUiA8LSA2MzcxICMgRWFydGggbWVhbiByYWRpdXMgW2ttXQojICAgZGVsdGEubG9uZyA8LSAobG9uZzIgLSBsb25nMSkKIyAgIGRlbHRhLmxhdCA8LSAobGF0MiAtIGxhdDEpCiMgICBhIDwtIHNpbihkZWx0YS5sYXQvMileMiArIGNvcyhsYXQxKSAqIGNvcyhsYXQyKSAqIHNpbihkZWx0YS5sb25nLzIpXjIKIyAgIGMgPC0gMiAqIGFzaW4obWluKDEsc3FydChhKSkpCiMgICBkID0gUiAqIGMgKiAwLjYyMTM3MSAjIDEga20gPSAwLjYyMTM3MSBtaWxlcwojICAgcmV0dXJuKGQpICMgRGlzdGFuY2UgaW4gbWlsZXMKIyB9CgpgYGAKCgpGZWF0dXJlIGVuZ2luZWVyaW5nLgoKQ3JlYXRpbmcgbW9yZSB2YXJpYWJsZXMuIENyZWF0aW5nIGEgQnVzRXZlbnQgcm93IG51bWJlciBmb3IgZnV0dXJlIGlkZW50aWZpY2F0aW9uIHB1cnBvc2VzLiBUaGVuLCBjcmVhdGluZyB2YXJpb3VzIHZhcmlhYmxlcyB0byBhbmFseXplIGRpc3RhbmNlIHRyYXZlbGVkIGFuZCBzcGVlZC4KYGBge3J9CgpBbGxEYXlzX0J1c0RheSA8LSBncm91cF9ieShBbGxEYXlzX0FkZFZhcnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIEJ1c19JRCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgRXZlbnRfVGltZV9EYXRlCiAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKEJ1c0RheV9FdmVudE51bSA9IHJvd19udW1iZXIoKSwgICMgdXNlZCB0byBpZGVudGlmeSBCdXMgbW92ZW1lbnRzIG9uIGEgcGFydGljdWxhciBkYXRlCiAgICAgICAgIAogICAgICAgICBSb3V0ZV9MYWcxID0gbGFnKFJvdXRlKSwgICMgdXNlZCBpbiBmdXR1cmUgYW5hbHlzZXMgdG8gaWRlbnRpZnkgUm91dGUgY2hhbmdlcwogICAgICAgICBSb3V0ZUFsdF9MYWcxID0gbGFnKFJvdXRlQWx0KSwgICMgdXNlZCBpbiBmdXR1cmUgYW5hbHlzZXMgdG8gaWRlbnRpZnkgUm91dGVBbHQgKGRpcmVjdGlvbikgY2hhbmdlcwogICAgICAgICAKICAgICAgICAgT2RvbWV0ZXJfRGlzdGFuY2VfTGFnMSA9IGxhZyhPZG9tZXRlcl9EaXN0YW5jZSksCiAgICAgICAgIAogICAgICAgICBMYXRpdHVkZV9MMSA9IGxhZyhMYXRpdHVkZSksCiAgICAgICAgIExvbmdpdHVkZV9MMSA9IGxhZyhMb25naXR1ZGUpLAogICAgICAgICAjIExhdF9SYWRpYW4gPSBMYXRpdHVkZSpwaS8xODAsCiAgICAgICAgICMgTG9uZ19SYWRpYW4gPSBMb25naXR1ZGUqcGkvMTgwLAogICAgICAgICAjIExhdF9SYWRpYW5fTDEgPSBsYWcoTGF0X1JhZGlhbiksCiAgICAgICAgICMgTG9uZ19SYWRpYW5fTDEgPSBsYWcoTG9uZ19SYWRpYW4pLAogICAgICAgICAKICAgICAgICAgIyBhY2NvdW50aW5nIGZvciBwb3RlbnRpYWwgbmVnYXRpdmUgZGlzdGFuY2VzCiAgICAgICAgIFRyYXZlbERpc3RhbmNlX0Z0ID0gaWZlbHNlKE9kb21ldGVyX0Rpc3RhbmNlID4gT2RvbWV0ZXJfRGlzdGFuY2VfTGFnMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgT2RvbWV0ZXJfRGlzdGFuY2UgLSBPZG9tZXRlcl9EaXN0YW5jZV9MYWcxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOQQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pID0gVHJhdmVsRGlzdGFuY2VfRnQgLyA1MjgwLCAjNSwyODAgZmVldCBpbiAxIG1pbGUKICAgICAgICAgCiAgICAgICAgICMgVHJhdmVsRGlzdGFuY2VfTWkyID0gZ2NkLmhmKGxvbmcxID0gTG9uZ19SYWRpYW5fTDEsCiAgICAgICAgICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhdDEgPSBMYXRfUmFkaWFuX0wxLAogICAgICAgICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb25nMiA9IExvbmdfUmFkaWFuLAogICAgICAgICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYXQyID0gTGF0X1JhZGlhbgogICAgICAgICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIAogICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzID0gCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgaWZlbHNlKChpcy5uYShMb25naXR1ZGVfTDEpIHwgaXMubmEoTGF0aXR1ZGVfTDEpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgICAgICAgIE5BLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXN0SGF2ZXJzaW5lKGNiaW5kKExvbmdpdHVkZV9MMSwgTGF0aXR1ZGVfTDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNiaW5kKExvbmdpdHVkZSwgTGF0aXR1ZGUpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICogMC4wMDA2MjEzNzEsICMgMC4wMDA2MjEzNzEgbWlsZXMgPSAxIG1ldGVyCiAgICAgICAgIAogICAgICAgICAjIGFjY291bnRpbmcgZm9yIHBvdGVudGlhbCBuZWdhdGl2ZSB0aW1lcwogICAgICAgICBUcmF2ZWxUaW1lX1NlYyA9IGFzLm51bWVyaWMoaWZlbHNlKEV2ZW50X1RpbWUgPiBsYWcoRGVwYXJ0dXJlX1RpbWUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWUgLSBsYWcoRGVwYXJ0dXJlX1RpbWUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5BCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRyYXZlbFRpbWVfSHIgPSBUcmF2ZWxUaW1lX1NlYyAvIDM2MDAsICMgMyw2MDAgc2Vjb25kcyBpbiAxIGhvdXIKICAgICAgICAgCiAgICAgICAgICMgYWNjb3VudGluZyBmb3IgcG90ZW50aWFsIG5lZ2F0aXZlIG9yIHplcm8gdHJhdmVsIHRpbWVzCiAgICAgICAgIFNwZWVkQXZnX01waCA9IGlmZWxzZShUcmF2ZWxUaW1lX0hyID4gMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pIC8gVHJhdmVsVGltZV9IciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5BCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIAogICAgICAgICBTdGFydF9JRCA9IGxhZyhTdG9wSURfQ2xlYW4pLAogICAgICAgICBTdGFydF9EZXNjID0gbGFnKFN0b3BfRGVzYyksCiAgICAgICAgIFN0YXJ0U3RvcF9JRCA9IGlmZWxzZShpcy5uYShTdGFydF9JRCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZSgiTlVMTCIsIFN0b3BJRF9DbGVhbiwgc2VwID0gIi0tIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXN0ZShTdGFydF9JRCwgU3RvcElEX0NsZWFuLCBzZXAgPSAiLS0iKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgKSAlPiUgCiAgYXMuZGF0YS5mcmFtZSgpCgoKcm0oQWxsRGF5c19BZGRWYXJzKQpzdHIoQWxsRGF5c19CdXNEYXkpCgojIHN1bW1hcnkoQWxsRGF5c19CdXNEYXkpCgojIFZpZXcodGFpbChBbGxEYXlzX0J1c0RheSwgNTApKQoKYGBgCgoKSW5zcGVjdGluZyBmb3IgaXNzdWVzIHdpdGggU3RhcnRTdG9wX0lEICh3aGVyZSB0aGUgdmFsdWUgaXMgZWl0aGVyIE5BIG9yIGNvbnRhaW5zIE5VTEwpLiBUaGV5IE9OTFkgZXhpc3Qgd2hlbiBCdXNEYXlfRXZlbnROdW0gPSAxICh3aGljaCBpcyBieSBkZXNpZ24pLiBTbyBldmVyeXRoaW5nIGxvb2tzIE9LLgpgYGB7cn0KClZpZXcoZ3JvdXBfYnkoQWxsRGF5c19CdXNEYXksCiAgICAgICAgICAgICAgU3RhcnRTdG9wX0lECiAgICAgICAgICAgICApICU+JSAKICAgICAgIHN1bW1hcmlzZSgKICAgICAgICAgQ250ID0gbigpCiAgICAgICApICU+JSAKICAgICAgIGFycmFuZ2UoZGVzYyhDbnQpCiAgICAgICAgICAgICAgKQogICAgKQoKVmlldyhmaWx0ZXIoQWxsRGF5c19CdXNEYXksCiAgICAgICAgICAgIChpcy5uYShTdGFydFN0b3BfSUQpIHwKICAgICAgICAgICAgICBzdHJfZGV0ZWN0KFN0YXJ0U3RvcF9JRCwgIk5VTEwiKQogICAgICAgICAgICApICYKICAgICAgICAgICAgICBCdXNEYXlfRXZlbnROdW0gIT0gMQogICAgICAgICAgICkKICAgICkKCmBgYAoKClN0YXRzIChxdWFudGlsZXMpIG92ZXJhbGwgZm9yIFRyYXZlbERpc3RhbmNlX01pLgpgYGB7cn0KClF1YW50aWxlc19kdCA8LSBBbGxEYXlzX0J1c0RheSAlPiUgCiAgbXV0YXRlKFREX01pX3EyID0gcXVhbnRpbGUoeCA9IFRyYXZlbERpc3RhbmNlX01pLCBwcm9icyA9IDAuMDIsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFREX01pX3E5OCA9IHF1YW50aWxlKHggPSBUcmF2ZWxEaXN0YW5jZV9NaSwgcHJvYnMgPSAwLjk4LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9TZWNfcTIgPSBxdWFudGlsZSh4ID0gVHJhdmVsVGltZV9TZWMsIHByb2JzID0gMC4wMiwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVFRfU2VjX3E5OCA9IHF1YW50aWxlKHggPSBUcmF2ZWxUaW1lX1NlYywgcHJvYnMgPSAwLjk4LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9Icl9xMiA9IHF1YW50aWxlKHggPSBUcmF2ZWxUaW1lX0hyLCBwcm9icyA9IDAuMDIsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFRUX0hyX3E5OCA9IHF1YW50aWxlKHggPSBUcmF2ZWxUaW1lX0hyLCBwcm9icyA9IDAuOTgsIG5hLnJtID0gVFJVRSkKICAgICAgICApICU+JSAKICBkYXRhLnRhYmxlKCkKCgpTdGF0cyA8LSBRdWFudGlsZXNfZHQgJT4lIAogIG11dGF0ZShURF9NaV9NZWFuID0gbWVhbihUcmF2ZWxEaXN0YW5jZV9NaSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVERfTWlfTWVhbl9GID0gbWVhbihUcmF2ZWxEaXN0YW5jZV9NaVtURF9NaV9xMiA8PSBUcmF2ZWxEaXN0YW5jZV9NaSAmIFRyYXZlbERpc3RhbmNlX01pIDw9IFREX01pX3E5OF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBURF9NaV9NZWQgPSBtZWRpYW4oVHJhdmVsRGlzdGFuY2VfTWksIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFREX01pX01lZF9GID0gbWVkaWFuKFRyYXZlbERpc3RhbmNlX01pW1REX01pX3EyIDw9IFRyYXZlbERpc3RhbmNlX01pICYgVHJhdmVsRGlzdGFuY2VfTWkgPD0gVERfTWlfcTk4XSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgVERfTWlfQ250ID0gc3VtKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSkKICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgVERfTWlfQ250X0YgPSBzdW0oIWlzLm5hKFRyYXZlbERpc3RhbmNlX01pW1REX01pX3EyIDw9IFRyYXZlbERpc3RhbmNlX01pICYgVHJhdmVsRGlzdGFuY2VfTWkgPD0gVERfTWlfcTk4XQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgCiAgICAgICAgIFRUX1NlY19NZWFuID0gbWVhbihUcmF2ZWxUaW1lX1NlYywgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVFRfU2VjX01lYW5fRiA9IG1lYW4oVHJhdmVsVGltZV9TZWNbVFRfU2VjX3EyIDw9IFRyYXZlbFRpbWVfU2VjICYgVHJhdmVsVGltZV9TZWMgPD0gVFRfU2VjX3E5OF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRUX1NlY19NZWQgPSBtZWRpYW4oVHJhdmVsVGltZV9TZWMsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFRUX1NlY19NZWRfRiA9IG1lZGlhbihUcmF2ZWxUaW1lX1NlY1tUVF9TZWNfcTIgPD0gVHJhdmVsVGltZV9TZWMgJiBUcmF2ZWxUaW1lX1NlYyA8PSBUVF9TZWNfcTk4XSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBUVF9TZWNfQ250ID0gc3VtKCFpcy5uYShUcmF2ZWxUaW1lX1NlYykKICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRUX1NlY19DbnRfRiA9IHN1bSghaXMubmEoVHJhdmVsVGltZV9TZWNbVFRfU2VjX3EyIDw9IFRyYXZlbFRpbWVfU2VjICYgVHJhdmVsVGltZV9TZWMgPD0gVFRfU2VjX3E5OF0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICksCgogICAgICAgICBUVF9Icl9NZWFuID0gbWVhbihUcmF2ZWxUaW1lX0hyLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9Icl9NZWFuX0YgPSBtZWFuKFRyYXZlbFRpbWVfSHJbVFRfSHJfcTIgPD0gVHJhdmVsVGltZV9IciAmIFRyYXZlbFRpbWVfSHIgPD0gVFRfSHJfcTk4XSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRUX0hyX01lZCA9IG1lZGlhbihUcmF2ZWxUaW1lX0hyLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9Icl9NZWRfRiA9IG1lZGlhbihUcmF2ZWxUaW1lX0hyW1RUX0hyX3EyIDw9IFRyYXZlbFRpbWVfSHIgJiBUcmF2ZWxUaW1lX0hyIDw9IFRUX0hyX3E5OF0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRUX0hyX0NudCA9IHN1bSghaXMubmEoVHJhdmVsVGltZV9IcikKICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgVFRfSHJfQ250X0YgPSBzdW0oIWlzLm5hKFRyYXZlbFRpbWVfSHJbVFRfSHJfcTIgPD0gVHJhdmVsVGltZV9IciAmIFRyYXZlbFRpbWVfSHIgPD0gVFRfSHJfcTk4XQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICkgJT4lIAogIGRhdGEuZnJhbWUoKQoKcm0oQWxsRGF5c19CdXNEYXkpCnJtKFF1YW50aWxlc19kdCkKc3RyKFN0YXRzKQojIFZpZXcoaGVhZChTdGF0cywgNTApKQoKYGBgCgoKU3RhdHMgZm9yIFN0YXJ0U3RvcF9JRC4KYGBge3J9CgpRdWFudGlsZXNfU1NfZHQgPC0gZ3JvdXBfYnkoU3RhdHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGFydFN0b3BfSUQKICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKFREX01pX1NTX3E1ID0gcXVhbnRpbGUoeCA9IFRyYXZlbERpc3RhbmNlX01pLCBwcm9icyA9IDAuMDUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFREX01pX1NTX3E5NSA9IHF1YW50aWxlKHggPSBUcmF2ZWxEaXN0YW5jZV9NaSwgcHJvYnMgPSAwLjk1LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9TZWNfU1NfcTUgPSBxdWFudGlsZSh4ID0gVHJhdmVsVGltZV9TZWMsIHByb2JzID0gMC4wNSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVFRfU2VjX1NTX3E5NSA9IHF1YW50aWxlKHggPSBUcmF2ZWxUaW1lX1NlYywgcHJvYnMgPSAwLjk1LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9Icl9TU19xNSA9IHF1YW50aWxlKHggPSBUcmF2ZWxUaW1lX0hyLCBwcm9icyA9IDAuMDUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFRUX0hyX1NTX3E5NSA9IHF1YW50aWxlKHggPSBUcmF2ZWxUaW1lX0hyLCBwcm9icyA9IDAuOTUsIG5hLnJtID0gVFJVRSkKICAgICAgICApICU+JSAKICBkYXRhLnRhYmxlKCkKCgpTdGF0c19TdFN0IDwtIGdyb3VwX2J5KFF1YW50aWxlc19TU19kdCwKICAgICAgICAgICAgICAgICAgICAgICBTdGFydFN0b3BfSUQKICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIG11dGF0ZShURF9NaV9TU19NZWFuID0gbWVhbihUcmF2ZWxEaXN0YW5jZV9NaSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVERfTWlfU1NfTWVhbl9GID0gbWVhbihUcmF2ZWxEaXN0YW5jZV9NaVtURF9NaV9TU19xNSA8PSBUcmF2ZWxEaXN0YW5jZV9NaSAmIFRyYXZlbERpc3RhbmNlX01pIDw9IFREX01pX1NTX3E5NV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBURF9NaV9TU19NZWQgPSBtZWRpYW4oVHJhdmVsRGlzdGFuY2VfTWksIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFREX01pX1NTX01lZF9GID0gbWVkaWFuKFRyYXZlbERpc3RhbmNlX01pW1REX01pX1NTX3E1IDw9IFRyYXZlbERpc3RhbmNlX01pICYgVHJhdmVsRGlzdGFuY2VfTWkgPD0gVERfTWlfU1NfcTk1XSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgVERfTWlfU1NfQ250ID0gc3VtKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgVERfTWlfU1NfQ250X0YgPSBzdW0oIWlzLm5hKFRyYXZlbERpc3RhbmNlX01pW1REX01pX1NTX3E1IDw9IFRyYXZlbERpc3RhbmNlX01pICYgVHJhdmVsRGlzdGFuY2VfTWkgPD0gVERfTWlfU1NfcTk1XQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgCiAgICAgICAgIFRUX1NlY19TU19NZWFuID0gbWVhbihUcmF2ZWxUaW1lX1NlYywgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVFRfU2VjX1NTX01lYW5fRiA9IG1lYW4oVHJhdmVsVGltZV9TZWNbVFRfU2VjX1NTX3E1IDw9IFRyYXZlbFRpbWVfU2VjICYgVHJhdmVsVGltZV9TZWMgPD0gVFRfU2VjX1NTX3E5NV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRUX1NlY19TU19NZWQgPSBtZWRpYW4oVHJhdmVsVGltZV9TZWMsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFRUX1NlY19TU19NZWRfRiA9IG1lZGlhbihUcmF2ZWxUaW1lX1NlY1tUVF9TZWNfU1NfcTUgPD0gVHJhdmVsVGltZV9TZWMgJiBUcmF2ZWxUaW1lX1NlYyA8PSBUVF9TZWNfU1NfcTk1XSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBUVF9TZWNfU1NfQ250ID0gc3VtKCFpcy5uYShUcmF2ZWxUaW1lX1NlYykpLAogICAgICAgICBUVF9TZWNfU1NfQ250X0YgPSBzdW0oIWlzLm5hKFRyYXZlbFRpbWVfU2VjW1RUX1NlY19TU19xNSA8PSBUcmF2ZWxUaW1lX1NlYyAmIFRyYXZlbFRpbWVfU2VjIDw9IFRUX1NlY19TU19xOTVdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCgogICAgICAgICBUVF9Icl9TU19NZWFuID0gbWVhbihUcmF2ZWxUaW1lX0hyLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9Icl9TU19NZWFuX0YgPSBtZWFuKFRyYXZlbFRpbWVfSHJbVFRfSHJfU1NfcTUgPD0gVHJhdmVsVGltZV9IciAmIFRyYXZlbFRpbWVfSHIgPD0gVFRfSHJfU1NfcTk1XSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRUX0hyX1NTX01lZCA9IG1lZGlhbihUcmF2ZWxUaW1lX0hyLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9Icl9TU19NZWRfRiA9IG1lZGlhbihUcmF2ZWxUaW1lX0hyW1RUX0hyX1NTX3E1IDw9IFRyYXZlbFRpbWVfSHIgJiBUcmF2ZWxUaW1lX0hyIDw9IFRUX0hyX1NTX3E5NV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRUX0hyX1NTX0NudCA9IHN1bSghaXMubmEoVHJhdmVsVGltZV9IcikpLAogICAgICAgICBUVF9Icl9TU19DbnRfRiA9IHN1bSghaXMubmEoVHJhdmVsVGltZV9IcltUVF9Icl9TU19xNSA8PSBUcmF2ZWxUaW1lX0hyICYgVHJhdmVsVGltZV9IciA8PSBUVF9Icl9TU19xOTVdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgKSAlPiUgCiAgZGF0YS5mcmFtZSgpCgpybShTdGF0cykKcm0oUXVhbnRpbGVzX1NTX2R0KQpzdHIoU3RhdHNfU3RTdCkKIyBWaWV3KGhlYWQoU3RhdHNfU3RTdCwgNTApKQoKYGBgCgoKU3RhdHMgZm9yIFN0YXJ0U3RvcF9JRCB3aXRoIEV2ZW50X1RpbWVfSHJHcm91cC4KYGBge3J9CgpRdWFudGlsZXNfU1NIR19kdCA8LSBncm91cF9ieShTdGF0c19TdFN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGFydFN0b3BfSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfSHJHcm91cAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIG11dGF0ZShURF9NaV9TU0hHX3E1ID0gcXVhbnRpbGUoeCA9IFRyYXZlbERpc3RhbmNlX01pLCBwcm9icyA9IDAuMDUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFREX01pX1NTSEdfcTk1ID0gcXVhbnRpbGUoeCA9IFRyYXZlbERpc3RhbmNlX01pLCBwcm9icyA9IDAuOTUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFRUX1NlY19TU0hHX3E1ID0gcXVhbnRpbGUoeCA9IFRyYXZlbFRpbWVfU2VjLCBwcm9icyA9IDAuMDUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFRUX1NlY19TU0hHX3E5NSA9IHF1YW50aWxlKHggPSBUcmF2ZWxUaW1lX1NlYywgcHJvYnMgPSAwLjk1LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9Icl9TU0hHX3E1ID0gcXVhbnRpbGUoeCA9IFRyYXZlbFRpbWVfSHIsIHByb2JzID0gMC4wNSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVFRfSHJfU1NIR19xOTUgPSBxdWFudGlsZSh4ID0gVHJhdmVsVGltZV9IciwgcHJvYnMgPSAwLjk1LCBuYS5ybSA9IFRSVUUpCiAgICAgICAgKSAlPiUgCiAgZGF0YS50YWJsZSgpCgoKU3RhdHNfU3RTdF9IckdycCA8LSBncm91cF9ieShRdWFudGlsZXNfU1NIR19kdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGFydFN0b3BfSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRXZlbnRfVGltZV9Ickdyb3VwCiAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBtdXRhdGUoVERfTWlfU1NIR19NZWFuID0gbWVhbihUcmF2ZWxEaXN0YW5jZV9NaSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVERfTWlfU1NIR19NZWFuX0YgPSBtZWFuKFRyYXZlbERpc3RhbmNlX01pW1REX01pX1NTSEdfcTUgPD0gVHJhdmVsRGlzdGFuY2VfTWkgJiBUcmF2ZWxEaXN0YW5jZV9NaSA8PSBURF9NaV9TU0hHX3E5NV0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgVERfTWlfU1NIR19NZWQgPSBtZWRpYW4oVHJhdmVsRGlzdGFuY2VfTWksIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFREX01pX1NTSEdfTWVkX0YgPSBtZWRpYW4oVHJhdmVsRGlzdGFuY2VfTWlbVERfTWlfU1NIR19xNSA8PSBUcmF2ZWxEaXN0YW5jZV9NaSAmIFRyYXZlbERpc3RhbmNlX01pIDw9IFREX01pX1NTSEdfcTk1XSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFREX01pX1NTSEdfQ250ID0gc3VtKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBURF9NaV9TU0hHX0NudF9GID0gc3VtKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaVtURF9NaV9TU0hHX3E1IDw9IFRyYXZlbERpc3RhbmNlX01pICYgVHJhdmVsRGlzdGFuY2VfTWkgPD0gVERfTWlfU1NIR19xOTVdCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgCiAgICAgICAgIFRUX1NlY19TU0hHX01lYW4gPSBtZWFuKFRyYXZlbFRpbWVfU2VjLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICBUVF9TZWNfU1NIR19NZWFuX0YgPSBtZWFuKFRyYXZlbFRpbWVfU2VjW1RUX1NlY19TU0hHX3E1IDw9IFRyYXZlbFRpbWVfU2VjICYgVHJhdmVsVGltZV9TZWMgPD0gVFRfU2VjX1NTSEdfcTk1XSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRUX1NlY19TU0hHX01lZCA9IG1lZGlhbihUcmF2ZWxUaW1lX1NlYywgbmEucm0gPSBUUlVFKSwKICAgICAgICAgVFRfU2VjX1NTSEdfTWVkX0YgPSBtZWRpYW4oVHJhdmVsVGltZV9TZWNbVFRfU2VjX1NTSEdfcTUgPD0gVHJhdmVsVGltZV9TZWMgJiBUcmF2ZWxUaW1lX1NlYyA8PSBUVF9TZWNfU1NIR19xOTVdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBUVF9TZWNfU1NIR19DbnQgPSBzdW0oIWlzLm5hKFRyYXZlbFRpbWVfU2VjKSksCiAgICAgICAgIFRUX1NlY19TU0hHX0NudF9GID0gc3VtKCFpcy5uYShUcmF2ZWxUaW1lX1NlY1tUVF9TZWNfU1NIR19xNSA8PSBUcmF2ZWxUaW1lX1NlYyAmIFRyYXZlbFRpbWVfU2VjIDw9IFRUX1NlY19TU0hHX3E5NV0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCgogICAgICAgICBUVF9Icl9TU0hHX01lYW4gPSBtZWFuKFRyYXZlbFRpbWVfSHIsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFRUX0hyX1NTSEdfTWVhbl9GID0gbWVhbihUcmF2ZWxUaW1lX0hyW1RUX0hyX1NTSEdfcTUgPD0gVHJhdmVsVGltZV9IciAmIFRyYXZlbFRpbWVfSHIgPD0gVFRfSHJfU1NIR19xOTVdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRUX0hyX1NTSEdfTWVkID0gbWVkaWFuKFRyYXZlbFRpbWVfSHIsIG5hLnJtID0gVFJVRSksCiAgICAgICAgIFRUX0hyX1NTSEdfTWVkX0YgPSBtZWRpYW4oVHJhdmVsVGltZV9IcltUVF9Icl9TU0hHX3E1IDw9IFRyYXZlbFRpbWVfSHIgJiBUcmF2ZWxUaW1lX0hyIDw9IFRUX0hyX1NTSEdfcTk1XSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgIFRUX0hyX1NTSEdfQ250ID0gc3VtKCFpcy5uYShUcmF2ZWxUaW1lX0hyKSksCiAgICAgICAgIFRUX0hyX1NTSEdfQ250X0YgPSBzdW0oIWlzLm5hKFRyYXZlbFRpbWVfSHJbVFRfSHJfU1NIR19xNSA8PSBUcmF2ZWxUaW1lX0hyICYgVHJhdmVsVGltZV9IciA8PSBUVF9Icl9TU0hHX3E5NV0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgKSAlPiUgCiAgZGF0YS5mcmFtZSgpCgpybShTdGF0c19TdFN0KQpybShRdWFudGlsZXNfU1NIR19kdCkKc3RyKFN0YXRzX1N0U3RfSHJHcnApCiMgVmlldyhoZWFkKFN0YXRzX1N0U3RfSHJHcnAsIDUwKSkKCmBgYAoKCkZlYXR1cmUgZW5naW5lZXJpbmcuCgpDcmVhdGluZyBhIEJ1c0V2ZW50Um91dGUgcm93IG51bWJlciwgYW5kIGEgUm91dGVBbHRfTGFnMSBpbmRpY2F0b3IgZm9yIGZ1dHVyZSBpZGVudGlmaWNhdGlvbiBwdXJwb3Nlcy4gCmBgYHtyfQoKIyBybShRdWFudGlsZXNfZHQpCiMgcm0oUXVhbnRpbGVzX1NTX2R0KQojIHJtKEFsbERheXNfQnVzRGF5KQojIHJtKFF1YW50aWxlc19TU0hHX2R0KQojIHJtKFN0YXRzX1N0U3QpCgojIEFsbERheXNfQnVzRGF5Um91dGUgPC0gZ3JvdXBfYnkoU3RhdHNfU3RTdF9IckdycCwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEJ1c19JRCwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfRGF0ZSwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJvdXRlCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAojICAgbXV0YXRlKFJvdXRlQWx0X0xhZzIgPSBsYWcoUm91dGVBbHQpICAjIHVzZWQgaW4gZnV0dXJlIGFuYWx5c2VzIHRvIGlkZW50aWZ5IFJvdXRlQWx0IChkaXJlY3Rpb24pIGNoYW5nZXMKIyAgICAgICAgICAKIyAgICAgICAgICAjIE9kb21ldGVyX0Rpc3RhbmNlX0xhZzEgPSBsYWcoT2RvbWV0ZXJfRGlzdGFuY2UpLAojICAgICAgICAgICMgCiMgICAgICAgICAgIyAjIGFjY291bnRpbmcgZm9yIHBvdGVudGlhbCBuZWdhdGl2ZSBkaXN0YW5jZXMKIyAgICAgICAgICAjIFRyYXZlbERpc3RhbmNlX0Z0ID0gaWZlbHNlKE9kb21ldGVyX0Rpc3RhbmNlID49IE9kb21ldGVyX0Rpc3RhbmNlX0xhZzEsCiMgICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICBPZG9tZXRlcl9EaXN0YW5jZSAtIE9kb21ldGVyX0Rpc3RhbmNlX0xhZzEsCiMgICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICBOQQojICAgICAgICAgICMgICAgICAgICAgICAgICAgICAgICAgICAgICApLAojICAgICAgICAgICMgVHJhdmVsRGlzdGFuY2VfTWkgPSBUcmF2ZWxEaXN0YW5jZV9GdCAvIDUyODAsICM1LDI4MCBmZWV0IGluIDEgbWlsZQojICAgICAgICAgICMgCiMgICAgICAgICAgIyAjIGFjY291bnRpbmcgZm9yIHBvdGVudGlhbCBuZWdhdGl2ZSB0aW1lcwojICAgICAgICAgICMgVHJhdmVsVGltZV9TZWMgPSBhcy5udW1lcmljKGlmZWxzZShFdmVudF9UaW1lID49IGxhZyhEZXBhcnR1cmVfVGltZSksCiMgICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWUgLSBsYWcoRGVwYXJ0dXJlX1RpbWUpLAojICAgICAgICAgICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOQQojICAgICAgICAgICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKIyAgICAgICAgICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiMgICAgICAgICAgIyBUcmF2ZWxUaW1lX0hyID0gVHJhdmVsVGltZV9TZWMgLyAzNjAwLCAjIDMsNjAwIHNlY29uZHMgaW4gMSBob3VyCiMgICAgICAgICAgIyAKIyAgICAgICAgICAjICMgYWNjb3VudGluZyBmb3IgcG90ZW50aWFsIG5lZ2F0aXZlIG9yIHplcm8gdHJhdmVsIHRpbWVzCiMgICAgICAgICAgIyBTcGVlZEF2Z19NcGggPSBpZmVsc2UoVHJhdmVsVGltZV9IciA+IDAsCiMgICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWkgLyBUcmF2ZWxUaW1lX0hyLAojICAgICAgICAgICMgICAgICAgICAgICAgICAgICAgICAgIE5BCiMgICAgICAgICAgIyAgICAgICAgICAgICAgICAgICAgICApCiMgICAgICAgICApICU+JSAKIyAgIGRhdGEuZnJhbWUoKQojIAojIHJtKFN0YXRzX1N0U3RfSHJHcnApCiMgc3RyKEFsbERheXNfQnVzRGF5Um91dGUpCgpgYGAKCgpGZWF0dXJlIGVuZ2luZWVyaW5nLgoKQ2FsY3VsYXRpbmcgYSB2YXJpYWJsZSB0byBrbm93IGlmIHRoZSBSb3V0ZUFsdCBjaGFuZ2VkLiBDb3VsZCBiZSB1c2VmdWwgaW4gaGVscGluZyBpZGVudGlmeWluZyB3ZWlyZG5lc3MgaW4gY2FsY3VsYXRlZCBkaXN0YW5jZXMgYW5kIHNwZWVkcy4KYGBge3J9CgojIHJtKFN0YXRzX1N0U3RfSHJHcnApCgpBbGxEYXlzX0RpckNoYW5nZSA8LSBTdGF0c19TdFN0X0hyR3JwICU+JSAgIyBBbGxEYXlzX0J1c0RheVJvdXRlICU+JSAKICBtdXRhdGUoUnRlQ2hhbmdlID0gaWZlbHNlKFJvdXRlID09IFJvdXRlX0xhZzEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU2FtZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2hhbmdlIgogICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBSdGVDaGFuZ2UyID0gZmFjdG9yKGlmZWxzZShpcy5uYShSdGVDaGFuZ2UpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2hhbmdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUnRlQ2hhbmdlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgRGlyQ2hhbmdlID0gaWZlbHNlKFJvdXRlQWx0ID09IFJvdXRlQWx0X0xhZzEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU2FtZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2hhbmdlIgogICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICBEaXJDaGFuZ2UyID0gZmFjdG9yKGlmZWxzZShpcy5uYShEaXJDaGFuZ2UpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ2hhbmdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRGlyQ2hhbmdlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICkKCiMgcm0oQWxsRGF5c19CdXNEYXlSb3V0ZSkKcm0oU3RhdHNfU3RTdF9IckdycCkKc3RyKEFsbERheXNfRGlyQ2hhbmdlKQoKVmlldyhmaWx0ZXIoQWxsRGF5c19EaXJDaGFuZ2UsCiAgICAgICAgICAgIGJldHdlZW4oUm93TnVtX09HLCAyNTcwMDYwLCAyNTcwMDgwKQogICAgICAgICAgICkgJT4lIAogICAgICAgc2VsZWN0KC1tYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICAgICkKICAgICkKCmBgYAoKClJlLW9yZGVyaW5nIHRoZSB2YXJpYWJsZXMgdG8gZWFzZSB3aXRoIGNvbXByZWhlbnNpb24uCmBgYHtyfQoKQWxsRGF5c19OZXdPcmRlciA8LSAgc2VsZWN0KEFsbERheXNfRGlyQ2hhbmdlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgUm93TnVtX09HLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVW5pcXVlTGF0TG5nLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGFydFN0b3BfSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBCdXNEYXlfRXZlbnROdW0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBCdXNfSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBSb3V0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJ0ZUNoYW5nZTIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBSb3V0ZUFsdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgUm91dGVBbHRfTGFnMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIERpckNoYW5nZTIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBSb3V0ZV9EaXJlY3Rpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdG9wX1NlcXVlbmNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RhcnRfSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGFydF9EZXNjLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBTdG9wX0lELAogICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RvcElEX0NsZWFuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RvcElEX0luZGljYXRvciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0b3BfRGVzYywKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvdW50cnlDb2RlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RvcF9TdGF0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0b3BfQ291bnR5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RvcF9DaXR5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RvcF9aaXAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UeXBlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgRXZlbnRfRGVzY3JpcHRpb24sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX1lyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgRXZlbnRfVGltZV9NdGgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0RhdGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0RheSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfSHIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0hyR3JvdXAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX01pbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBEZXBhcnR1cmVfVGltZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIER3ZWxsX1RpbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBEd2VsbF9UaW1lMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIERlbHRhX1RpbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBMYXRpdHVkZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIExvbmdpdHVkZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIEhlYWRpbmcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBPZG9tZXRlcl9EaXN0YW5jZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIE9kb21ldGVyX0Rpc3RhbmNlX0xhZzEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBPZG9tZXRlcl9EaXN0YW5jZV9NaSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX0Z0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfcTIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9xOTgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU19xNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTX3E5NSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTSEdfcTUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU0hHX3E5NSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX01lYW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9NZWFuX0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU19NZWFuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfU1NfTWVhbl9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfU1NIR19NZWFuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfU1NIR19NZWFuX0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9NZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9NZWRfRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTX01lZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTX01lZF9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfU1NIR19NZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU0hHX01lZF9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfQ250LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfQ250X0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU19DbnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU19DbnRfRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTSEdfQ250LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfU1NIR19DbnRfRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX3EyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX3E5OCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU19xNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU19xOTUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NIR19xNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU0hHX3E5NSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19NZWFuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX01lYW5fRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU19NZWFuLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX1NTX01lYW5fRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU0hHX01lYW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NIR19NZWFuX0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfTWVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX01lZF9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX1NTX01lZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU19NZWRfRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU0hHX01lZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU0hHX01lZF9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX0NudCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19DbnRfRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU19DbnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NfQ250X0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NIR19DbnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NIR19DbnRfRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfSHIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9Icl9xMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX3E5OCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX1NTX3E1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfSHJfU1NfcTk1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfSHJfU1NIR19xNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX1NTSEdfcTk1LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfSHJfTWVhbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX01lYW5fRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX1NTX01lYW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9Icl9TU19NZWFuX0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9Icl9TU0hHX01lYW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9Icl9TU0hHX01lYW5fRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX01lZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX01lZF9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfSHJfU1NfTWVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfSHJfU1NfTWVkX0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9Icl9TU0hHX01lZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX1NTSEdfTWVkX0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9Icl9DbnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9Icl9DbnRfRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX1NTX0NudCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX0hyX1NTX0NudF9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfSHJfU1NIR19DbnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9Icl9TU0hHX0NudF9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgU3BlZWRBdmdfTXBoCiAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCnJtKEFsbERheXNfRGlyQ2hhbmdlKQpzdHIoc2VsZWN0KEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgLW1hdGNoZXMoIihxKDJ8NXwoOTUpfCg5OCkpKXxNZWFufE1lZHxDbnQiKQogICAgICAgICAgKQogICApCnN0cihBbGxEYXlzX05ld09yZGVyKQoKIyBWaWV3KGhlYWQoQWxsRGF5c19OZXdPcmRlciwgNTAwKSkKIyBWaWV3KHRhaWwoQWxsRGF5c19OZXdPcmRlciwgNTAwKSkKCmBgYAoKClN1bW1hcml6aW5nIHRoZSBkYXRhIHRvIGhlbHAgc3BvdCBhbm9tb2xpZXMuCmBgYHtyfQoKVmlldyhncm91cF9ieShBbGxEYXlzX05ld09yZGVyLAogICAgICAgICAgICAgIFN0b3BfQ2l0eSkgJT4lIAogICAgICAgc3VtbWFyaXNlKENudF9OdW0gPSBuKCksCiAgICAgICAgICAgICAgICAgQ250X1BjdCA9IDEwMCpDbnRfTnVtIC8gKG5yb3coQWxsRGF5c19OZXdPcmRlcikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICApICU+JSAKICAgICAgIGFycmFuZ2UoZGVzYyhDbnRfTnVtKSkKKQoKc3VtbWFyeShBbGxEYXlzX05ld09yZGVyKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxEaXN0YW5jZV9NaS4KClZpZXcoVHJhdkRpc3RNaV9QY3RpbGVzKTogOTklIG9mIFRyYXZlbERpc3RhbmNlX01pIGFyZSBhYm91dCAxIG1pbGUgb3IgbGVzcy4uLmJ1dCBzb21lIHdlaXJkIFRyYXZlbERpc3RhbmNlX01pIHZhbHVlcyAoZS5nLiwgNTg0IG1pbGVzIHRyYXZlbGVkKSBleGlzdC4KYGBge3J9CgpUcmF2RGlzdE1pX050aWxlIDwtIGFzLmRhdGEuZnJhbWUoQWxsRGF5c19OZXdPcmRlciRUcmF2ZWxEaXN0YW5jZV9NaSkgJT4lIAogIG11dGF0ZSgjUGN0aWxlID0gbnRpbGUoQWxsRGF5c19OZXdPcmRlciRUcmF2ZWxEaXN0YW5jZV9NaSwgMTAwKSwKICAgICAgICAgI01pblIgPSBtaW5fcmFuayhBbGxEYXlzX05ld09yZGVyJFRyYXZlbERpc3RhbmNlX01pKSwKICAgICAgICAgUGN0UiA9IHBlcmNlbnRfcmFuayhBbGxEYXlzX05ld09yZGVyJFRyYXZlbERpc3RhbmNlX01pKSwKICAgICAgICAgUGN0Ul9Sb3VuZCA9IHJvdW5kKFBjdFIsIDIpCiAgICAgICAgKSAKCmNvbG5hbWVzKFRyYXZEaXN0TWlfTnRpbGUpWzFdIDwtICJUcmF2ZWxEaXN0YW5jZV9NaSIKIyBzdHIoVHJhdkRpc3RNaV9OdGlsZSkKClRyYXZEaXN0TWlfTnRpbGVfUm93cyA8LSBucm93KFRyYXZEaXN0TWlfTnRpbGUpCgojIFZpZXcodGFpbChUcmF2RGlzdE1pX050aWxlLCA1MDApKQoKClRyYXZEaXN0TWlfUGN0aWxlcyA8LSBncm91cF9ieShUcmF2RGlzdE1pX050aWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUGN0Ul9Sb3VuZAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzdW1tYXJpc2UoCiAgICBNaW5UcmF2RGlzdE1pQXRQY3RpbGUgPSBtaW4oVHJhdmVsRGlzdGFuY2VfTWkpLAogICAgQ250c0F0UGN0aWxlID0gbigpLAogICAgUGN0c0F0UGN0aWxlID0gQ250c0F0UGN0aWxlIC8gVHJhdkRpc3RNaV9OdGlsZV9Sb3dzCiAgKSAlPiUgCiAgbXV0YXRlKEN1bVN1bVBBdFAgPSBjdW1zdW0oUGN0c0F0UGN0aWxlKQogICAgICAgICkKCnJtKFRyYXZEaXN0TWlfTnRpbGUpCnJtKFRyYXZEaXN0TWlfTnRpbGVfUm93cykKClZpZXcoVHJhdkRpc3RNaV9QY3RpbGVzKQpUcmF2RGlzdE1pX1BjdGlsZXMKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgVHJhdmVsRGlzdGFuY2VfTWkuCgpXaHkgYXJlIHNvbWUgVHJhdmVsRGlzdGFuY2VfTWkgIk5BIj8gSXQgbG9va3MgbGlrZSBwYXJ0aWFsbHkgYmVjYXVzZSB0aGUgcmVjb3JkcyBhcmUgdGhlIGZpcnN0IHRyaXAgb2YgdGhlIGRheSAoZm9yIHRoYXQgYnVzKSwgc28gSSBwdXJwb3NlZnVsbHkgc2V0IHRoZSBkaXN0YW5jZSB0byAiTkEiLiBBbm90aGVyIHJlYXNvbiBpcyBkdWUgdG8gdGhlIG9kb21ldGVyIHJlY29yZGluZyBhIHZhbHVlIGxlc3MgdGhhbiB0aGUgcHJldmlvdXMgb2RvbWV0ZXIgcmVjb3JkaW5nLiBJbiBtb3N0IGNhc2VzLCBJIGhhdmUgbm8gZXhwbGFuYXRpb24gZm9yIHRoaXMgLSB0aG91Z2ggSSBoYXZlIG9ic2VydmVkIGFib3V0IDY3JSBvZiBhbGwgaW5zdGFuY2VzIHdoZXJlIFRyYXZlbERpc3RhbmNlX01pIGlzIE5BIChvdGhlciB0aGFuIGJlY2F1c2UgaXQncyB0aGUgZmlyc3QgcmVjb3JkIG9mIHRoZSBkYXkpIGFyZSBpbnN0YW5jZXMgd2hlcmUgRGlyQ2hhbmdlMiBpcyAiQ2hhbmdlIi4gVGhpcyBpcyB3ZWlyZCBhbmQgc2hvdWxkIGJlIGFza2VkIHRvIFdNQVRBLgpgYGB7cn0KCiMgVmlldyhoZWFkKEFsbERheXNfTmV3T3JkZXIsIDUwMCkpCgpWaWV3KGZpbHRlcihBbGxEYXlzX05ld09yZGVyLAogICAgICAgICAgICBCdXNEYXlfRXZlbnROdW0gIT0gMSAjIFdoZW4gQnVzRGF5X0V2ZW50TnVtID09IDEsIFRyYXZlbERpc3RhbmNlX01pIGlzIE5BIGJ5IGRlc2lnbiAoZG9uJ3Qgd2FudCB0byBjYWxjdWxhdGUgZGlzdGFuY2UgYmFzZWQgb24geWVzdGVyZGF5J3MgcG9zaXRpb24pCiAgICAgICAgICAgKSAlPiUgCiAgICAgICBncm91cF9ieShTdGFydFN0b3BfSUQpICU+JSAKICAgICAgIHN1bW1hcmlzZShDbnRzID0gc3VtKGlzLm5hKFRyYXZlbERpc3RhbmNlX01pKQogICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICApICU+JSAKICAgICAgIGFycmFuZ2UoZGVzYyhDbnRzKQogICAgICAgICAgICAgICkKICAgICkKClZpZXcoZmlsdGVyKEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgIFN0YXJ0U3RvcF9JRCA9PSAiMTAwMDI0NS0tMTAwMDIxMSIKICAgICAgICAgICApICU+JSAKICAgICAgIHNlbGVjdChSb3dOdW1fT0csCiAgICAgICAgICAgICAgU3RhcnRTdG9wX0lELAogICAgICAgICAgICAgIEV2ZW50X1RpbWUsCiAgICAgICAgICAgICAgRXZlbnRfVGltZV9Ickdyb3VwLAogICAgICAgICAgICAgIEJ1c19JRCwKICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaSwKICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzLAogICAgICAgICAgICAgIFREX01pX1NTX01lYW4sCiAgICAgICAgICAgICAgVERfTWlfU1NfTWVhbl9GLAogICAgICAgICAgICAgIFREX01pX1NTSEdfTWVhbiwKICAgICAgICAgICAgICBURF9NaV9TU0hHX01lYW5fRiwKICAgICAgICAgICAgICBURF9NaV9TU19NZWQsCiAgICAgICAgICAgICAgVERfTWlfU1NfTWVkX0YsCiAgICAgICAgICAgICAgVERfTWlfU1NIR19NZWQsCiAgICAgICAgICAgICAgVERfTWlfU1NIR19NZWRfRiwKICAgICAgICAgICAgICBURF9NaV9TU19DbnQsCiAgICAgICAgICAgICAgVERfTWlfU1NfQ250X0YsCiAgICAgICAgICAgICAgVERfTWlfU1NIR19DbnQsCiAgICAgICAgICAgICAgVERfTWlfU1NIR19DbnRfRgogICAgICAgICAgICAgICkgJT4lIAogICAgICAgbXV0YXRlKFJhdGlvX01lYW5Ub0h2cnMgPSBURF9NaV9TU19NZWFuIC8gVHJhdmVsRGlzdGFuY2VfTWlfSHZycykgJT4lIAogICAgICAgYXJyYW5nZShFdmVudF9UaW1lKQogICAgKQoKVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgaXMubmEoVHJhdmVsRGlzdGFuY2VfTWkpCiAgICAgICAgICAgKQogICAgKQoKIyBUaGVzZSByZWNvcmRzIGFyZSBOQSBiZWN1YXNlIHRoZSByZWNvcmQgaXMgdGhlIGZpcnN0IHJlY29yZCBvZiB0aGUgZGF5ICh0aGUgRXZlbnRfVGltZV9EYXRlKQpWaWV3KGZpbHRlcihBbGxEYXlzX05ld09yZGVyLAogICAgICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywgMzI2LCAzNDYpIHwgIyAzMzYKICAgICAgICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywgNTkxLCA2MTEpIHwgIyA2MDEKICAgICAgICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywgODQ1LCA4NjUpICMgODU1CiAgICAgICAgICAgKQogICAgKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxEaXN0YW5jZV9NaS4KClRoZXNlIHJlY29yZHMgYXJlIE5BIGJlY3Vhc2UgdGhlIGN1cnJlbnQgcmVjb3JkIG9kb21ldGVyIGlzIGxlc3MgdGhhbiB0aGUgcHJldmlvdXMgcmVjb3JkIG9kb21ldGVyLiBUaGVvcmV0aWNhbGx5LCB0aGlzIHNob3VsZCBOT1QgaGFwcGVuLiBNZTogaXQgYXBwZWFycyB0aGF0IGFib3V0IDY3JSBvZiBhbGwgaW5zdGFuY2VzIHdoZXJlIFRyYXZlbERpc3RhbmNlX01pIGlzIE5BIChvdGhlciB0aGFuIGJlY2F1c2UgaXQncyB0aCBmaXJzdCByZWNvcmQgb2YgdGhlIGRheSkgYXJlIGluc3RhbmNlcyB3aGVyZSBEaXJDaGFuZ2UyIGlzICJDaGFuZ2UiLiBUaGlzIGlzIHdlaXJkIGFuZCBzaG91bGQgYmUgYXNrZWQgdG8gV01BVEEuCmBgYHtyfQoKVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgYmV0d2VlbihSb3dOdW1fT0csIDE5NCwgMjE0KSB8ICMgMjA0CiAgICAgICAgICAgICAgYmV0d2VlbihSb3dOdW1fT0csIDQ0MCwgNDYwKSB8ICMgNDUwCiAgICAgICAgICAgICAgYmV0d2VlbihSb3dOdW1fT0csIDQ3OCwgNDk4KSB8ICMgNDg4CiAgICAgICAgICAgICAgYmV0d2VlbihSb3dOdW1fT0csIDUxMCwgNTMwKSAjIDUyMAogICAgICAgICAgICkKICAgICkKClRlc3RUYWJsZSA8LSBmaWx0ZXIoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgICAgICAgICBCdXNEYXlfRXZlbnROdW0gIT0gMQogICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKFRyYXZlbERpc3RhbmNlX05BID0gYXMuZmFjdG9yKGlmZWxzZShpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVHJ1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRmFsc2UiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgKSAlPiUKICBncm91cF9ieShEaXJDaGFuZ2UyLCBUcmF2ZWxEaXN0YW5jZV9OQSkgJT4lCiAgc3VtbWFyaXNlKFRyYXZEaXN0TWlfTkFDbnRzID0gbigpCiAgICAgICAgICAgKQoKIyBUZXN0VGFibGUKClRlc3RUYWJsZV9TcHJlYWQgPC0gYXMuZGF0YS5mcmFtZShzcHJlYWQoVGVzdFRhYmxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX05BLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZEaXN0TWlfTkFDbnRzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHNlbGVjdChGYWxzZSwKICAgICAgICAgVHJ1ZQogICAgICAgICkKCnJvdy5uYW1lcyhUZXN0VGFibGVfU3ByZWFkKSA8LSBjKCJDaGFuZ2UiLCAiU2FtZSIpCiMgc3RyKFRlc3RUYWJsZV9TcHJlYWQpCiMgVGVzdFRhYmxlX1NwcmVhZAoKcHJvcC50YWJsZShhcy50YWJsZShhcy5tYXRyaXgoVGVzdFRhYmxlX1NwcmVhZCkKICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgMQogICAgICAgICAgKQoKcHJvcC50YWJsZShhcy50YWJsZShhcy5tYXRyaXgoVGVzdFRhYmxlX1NwcmVhZCkKICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgMgogICAgICAgICAgKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxEaXN0YW5jZV9NaS4KCkxldCdzIGxvb2sgYXQganVzdCB0aGUgVHJhdmVsRGlzdGFuY2VfTWkgdmFsdWVzIHRoYXQgYXJlIE5PVCAiTkEiLgpgYGB7cn0KCnJtKFRlc3RUYWJsZSwgVGVzdFRhYmxlX1NwcmVhZCkKClRyYXZlbERpc3RhbmNlX01pX05vTkEgPC0gZmlsdGVyKEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgVHJhdmVsRGlzdGFuY2VfTWkgIT0gMCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCgpkaW0oQWxsRGF5c19OZXdPcmRlcikKZGltKFRyYXZlbERpc3RhbmNlX01pX05vTkEpCm5yb3coQWxsRGF5c19OZXdPcmRlcikgLSBucm93KFRyYXZlbERpc3RhbmNlX01pX05vTkEpCgpzdHIoVHJhdmVsRGlzdGFuY2VfTWlfTm9OQSkKc3VtbWFyeShUcmF2ZWxEaXN0YW5jZV9NaV9Ob05BKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxEaXN0YW5jZV9NaS4KCkxldCdzIHBsb3QganVzdCB0aGUgVHJhdmVsRGlzdGFuY2VfTWkgdmFsdWVzIHRoYXQgYXJlIE5PVCAiTkEiLgpgYGB7cn0KClRyYXZEaXN0TWlfSGlzdERlbiA8LSBnZ3Bsb3Qoc2VsZWN0KFRyYXZlbERpc3RhbmNlX01pX05vTkEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IFRyYXZlbERpc3RhbmNlX01pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gLi5kZW5zaXR5Li4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICApICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuMDUsIGZpbGwgPSAibGlnaHRibHVlIiwgY29sb3VyID0gImdyZXk2MCIsIHNpemUgPSAwLjIpICsKICBnZW9tX2xpbmUoc3RhdCA9ICJkZW5zaXR5IiwgY29sb3VyID0gInJlZCIpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMCwgMS41KSwgeWxpbSA9IGMoMCwgNC4wKQogICAgICAgICAgICAgICAgICkgKwogIGxhYnModGl0bGUgPSAiVmFyaWF0aW9uIGluIERpc3RhbmNlIEJldHdlZW4gU3RvcHMiLAogICAgICAgeCA9ICJUcmF2ZWwgRGlzdGFuY2UgKG1pbGVzKSIsCiAgICAgICB5ID0gIkRlbnNpdHkiCiAgICAgICkKClRyYXZEaXN0TWlfSGlzdERlbgoKYGBgCgpJbnZlc3RpZ2F0aW9uIG9mIFRyYXZlbERpc3RhbmNlX01pLgoKTG9va2luZyBhdCB0aGUgZXh0cmVtZWx5IGxhcmdlIFRyYXZlbERpc3RhbmNlX01pIHZhbHVlcy4gU29tZSAoYXByb3ggMjclKSBvZiBUcmF2ZWxEaXN0YW5jZV9NaSB2YWx1ZXMgPiAxIG1pbGUgYXJlIHdoZW4gdGhlIERpckNoYW5nZTIgY2hhbmdlcy4uLmJ1dCB3aGF0IGFib3V0IHRoZSBvdGhlciB+NzMlPwpgYGB7cn0KCnJtKFRyYXZlbERpc3RhbmNlX01pX05vTkEpCgojIGV4YW1wbGVzIG9mIHdlaXJkbHkgbGFyZ2UgVHJhdmVsRGlzdGFuY2VfTWkKVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWkgPiAxLjE1ODcxMjEyMTIgIyAxLjE1ODcxMjEyMTIgaXMgdGhlIDk5dGggcGVyY2VudGlsZQogICAgICAgICAgICkgJT4lIAogICAgICAgYXJyYW5nZShkZXNjKFRyYXZlbERpc3RhbmNlX01pKQogICAgICAgICAgICAgICkKICAgICkKCgojIFdoeSBhcmUgdGhlc2UgZXh0cmVtZXM/ICBBaXJwb3J0cz8gIEJ1cyBjb2xsZWN0aW9uIHBvaW50cz8KVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywgNDk0MDQ0LCA0OTQwNjQpIHwgIyA0OTQwNTQKICAgICAgICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywgNDk0MjczLCA0OTQyOTMpIHwgIyA0OTQyODMKICAgICAgICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywgNDk0NjI2LCA0OTQ2NDYpIHwgIyA0OTQ2MzYKICAgICAgICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywgMTYxMDE1NiwgMTYxMDE3NikgfCAjIDE2MTAxNjYKICAgICAgICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywgMjA3MzA3NCwgMjA3MzA5NCkgIyAyMDczMDg0CiAgICAgICAgICAgKQogICAgKQoKIyBCZWZvcmUgUmVtb3ZpbmcgUnVucwojIFZpZXcoZmlsdGVyKEFsbERheXNfU29ydGVkLAojICAgICAgICAgICAgIGJldHdlZW4oUm93TnVtX09HLCA0OTQwNDQsIDQ5NDA2NCkgfCAjIDQ5NDA1NAojICAgICAgICAgICAgICAgYmV0d2VlbihSb3dOdW1fT0csIDQ5NDI3MywgNDk0MjkzKSB8ICMgNDk0MjgzCiMgICAgICAgICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywgNDk0NjI2LCA0OTQ2NDYpIHwgIyA0OTQ2MzYKIyAgICAgICAgICAgICAgIGJldHdlZW4oUm93TnVtX09HLCAxNjEwMTU2LCAxNjEwMTc2KSB8ICMgMTYxMDE2NgojICAgICAgICAgICAgICAgYmV0d2VlbihSb3dOdW1fT0csIDIwNzMwNzQsIDIwNzMwOTQpICMgMjA3MzA4NAojICAgICAgICAgICAgKQojICAgICApCgojIEFmdGVyIFJlbW92aW5nIFJ1bnMKIyBWaWV3KGZpbHRlcihBbGxEYXlzX0ZpcnN0U3RvcElELAojICAgICAgICAgICAgIGJldHdlZW4oUm93TnVtX09HLCA0OTQwNDQsIDQ5NDA2NCkgfCAjIDQ5NDA1NAojICAgICAgICAgICAgICAgYmV0d2VlbihSb3dOdW1fT0csIDQ5NDI3MywgNDk0MjkzKSB8ICMgNDk0MjgzCiMgICAgICAgICAgICAgICBiZXR3ZWVuKFJvd051bV9PRywgNDk0NjI2LCA0OTQ2NDYpIHwgIyA0OTQ2MzYKIyAgICAgICAgICAgICAgIGJldHdlZW4oUm93TnVtX09HLCAxNjEwMTU2LCAxNjEwMTc2KSB8ICMgMTYxMDE2NgojICAgICAgICAgICAgICAgYmV0d2VlbihSb3dOdW1fT0csIDIwNzMwNzQsIDIwNzMwOTQpICMgMjA3MzA4NAojICAgICAgICAgICAgKQojICAgICApCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFRyYXZlbERpc3RhbmNlX01pLgoKQW55IHJlbGF0aW9uIHdpdGggRGlyQ2hhbmdlMj8gIERvZXNuJ3QgbG9vayBhcyBpZiB0aGlzIGlzIHNvLgpgYGB7cn0KCkV4dHJlbWVUcmF2RGlzdCA8LSBmaWx0ZXIoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgICAgICAgICAgICAgICAhaXMubmEoVHJhdmVsRGlzdGFuY2VfTWkpCiAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBtdXRhdGUoVHJhdkRpc3RfRXh0cmVtZSA9IGlmZWxzZShUcmF2ZWxEaXN0YW5jZV9NaSA+IDEuMTU4NzEyMTIxMiwgIyAxLjE1ODcxMjEyMTIgaXMgdGhlIDk5dGggcGVyY2VudGlsZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJUcnVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRmFsc2UiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgZ3JvdXBfYnkoRGlyQ2hhbmdlMiwgVHJhdkRpc3RfRXh0cmVtZSkgJT4lIAogIHN1bW1hcmlzZShUcmF2RGlzdE1JX0V4dENudHMgPSBuKCkKICAgICAgICAgICApCgojIEV4dHJlbWVUcmF2RGlzdAoKCkV4dHJlbWVUcmF2RGlzdF9TcHJlYWQgPC0gYXMuZGF0YS5mcmFtZShzcHJlYWQoRXh0cmVtZVRyYXZEaXN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZEaXN0X0V4dHJlbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdkRpc3RNSV9FeHRDbnRzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHNlbGVjdChGYWxzZSwKICAgICAgICAgVHJ1ZQogICAgICAgICkKCnJvdy5uYW1lcyhFeHRyZW1lVHJhdkRpc3RfU3ByZWFkKSA8LSBjKCJDaGFuZ2UiLCAiU2FtZSIpCiMgc3RyKEV4dHJlbWVUcmF2RGlzdF9TcHJlYWQpCiMgRXh0cmVtZVRyYXZEaXN0X1NwcmVhZAoKcHJvcC50YWJsZShhcy50YWJsZShhcy5tYXRyaXgoRXh0cmVtZVRyYXZEaXN0X1NwcmVhZCkKICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgMQogICAgICAgICAgKQoKcHJvcC50YWJsZShhcy50YWJsZShhcy5tYXRyaXgoRXh0cmVtZVRyYXZEaXN0X1NwcmVhZCkKICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgMgogICAgICAgICAgKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxEaXN0YW5jZV9NaS4KCkxvb2tpbmcgYXQgc3BlY2lmaWMgYnVzZXMgYW5kIFN0YXJ0U3RvcF9JRC4KYGBge3J9CgpybShFeHRyZW1lVHJhdkRpc3QsIEV4dHJlbWVUcmF2RGlzdF9TcHJlYWQpCgpWaWV3KGFycmFuZ2UoZ3JvdXBfYnkoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgICAgICAgICAgIEJ1c19JRAogICAgICAgICAgICAgICAgICAgICApICU+JSAKICAgICAgICAgICAgICAgc3VtbWFyaXNlKERpc3RUcmF2X01lYW4gPSBtZWFuKFRyYXZlbERpc3RhbmNlX01pLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICAgICAgICAgICAgICAgRGlzdFRyYXZfTWVkID0gbWVkaWFuKFRyYXZlbERpc3RhbmNlX01pLCBuYS5ybSA9IFRSVUUpCiAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICBkZXNjKERpc3RUcmF2X01lZCkKICAgICAgICAgICAgKQogICAgKQoKCiMgZXhhbXBsZSBvZiBleHRyZW1lbHkgc21hbGwgVHJhdmVsRGlzdGFuY2VfTWkgdmFsdWVzIChsb29rcyBsaWtlIHRoZSBvZG9tZXRlciB3YXNuJ3QgZnVuY3Rpb25pbmcpClZpZXcoZmlsdGVyKEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgIEJ1c19JRCA9PSA2MTExIHwKICAgICAgICAgICAgICBCdXNfSUQgPT0gNzIwMSB8CiAgICAgICAgICAgICAgQnVzX0lEID09IDgwNTgKICAgICAgICAgICApICU+JSAKICAgICAgIGFycmFuZ2UoQnVzX0lELCBFdmVudF9UaW1lKQogICAgKQoKClZpZXcoYXJyYW5nZShncm91cF9ieShBbGxEYXlzX05ld09yZGVyLAogICAgICAgICAgICAgICAgICAgICAgU3RhcnRTdG9wX0lECiAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogICAgICAgICAgICAgICBzdW1tYXJpc2UoRGlzdFRyYXZfTWVhbiA9IG1lYW4oVHJhdmVsRGlzdGFuY2VfTWksIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgICAgICAgICAgICAgICBEaXN0VHJhdl9NZWQgPSBtZWRpYW4oVHJhdmVsRGlzdGFuY2VfTWksIG5hLnJtID0gVFJVRSkKICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgIGRlc2MoRGlzdFRyYXZfTWVkKQogICAgICAgICAgICApCiAgICApCgojIGV4YW1wbGUgb2YgZXh0cmVtZWx5IGxhcmdlIFRyYXZlbERpc3RhbmNlX01pIHZhbHVlcy4uLm5vIGlkZWEgd2h5Li4uClZpZXcoZmlsdGVyKEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgIFN0YXJ0U3RvcF9JRCA9PSAiMTAwMzY2NS0tMTIiIHwKICAgICAgICAgICAgICBTdGFydFN0b3BfSUQgPT0gIjEwMDM2NjUtLTUwMDE5MjUiIHwKICAgICAgICAgICAgICBTdGFydFN0b3BfSUQgPT0gIjMwMDEwMzgtLTMwMDI1NjUiCiAgICAgICAgICAgKSAlPiUgCiAgICAgICBhcnJhbmdlKFN0YXJ0U3RvcF9JRCwgRXZlbnRfVGltZSkKICAgICkKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgVHJhdmVsRGlzdGFuY2VfTWkgJiBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcuCgpJZiBUcmF2ZWxEaXNudGFjZV9NaSBpcyBiZWxvdyB0aGUgNXRoIHBlcmNlbnRpbGUgZm9yIHRoYXQgU3RhcnRTdG9wX0lELCBvciBpZiBUcmF2ZWxEaXNudGFjZV9NaSBpcyBhYm92ZSB0aGUgOTV0aCBwZXJjZW50aWxlIGZvciB0aGF0IFN0YXJ0U3RvcF9JRCwgb3IgaWYgVHJhdmVsRGlzdGFuY2VfTWkgaXMgTkEgKHdoZW4gdGhlIEJ1c0RheV9FdmVudE51bSAhPTEpLCBjb25zaWRlciB0aGlzIGFuIG91dGxpZXIuICBJbiB0aGlzIGNhc2UsIHJlcGxhY2UgdGhlIHZhbHVlIHdpdGggdGhlIG1lYW4gZm9yIHRoYXQgU3RhcnRTdG9wX0lEIGFuZCBIb3VyR3JvdXAgKFREX01pX1NTSEdfTWVhbl9GKSwgb3IgaWYgdGhlcmUgYXJlIG5vdCBlbm91Z2ggdmFsdWVzIGF0IHRoZSBIb3VyR3JvdXAgbGV2ZWwsIHJlcGxhY2UgaXQgd2l0aCB0aGUgbWVhbiBmb3IgdGhhdCBTdGFydFN0b3BfSUQuCmBgYHtyfQoKIyBWaWV3KHRhaWwoQWxsRGF5c19OZXdPcmRlciwgNTAwKSkKCkFsbERheXNfTmV3VHJhdmVsRGlzdCA8LSAKICBtdXRhdGUoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfTmV3ID0gaWZlbHNlKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSkgJiAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKFRyYXZlbERpc3RhbmNlX01pIDwgVERfTWlfU1NIR19xNSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pID4gVERfTWlfU1NIR19xOTUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTSEdfQ250X0YgPj0gMjAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU0hHX01lYW5fRiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSkgJiAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKFRyYXZlbERpc3RhbmNlX01pIDwgVERfTWlfU1NIR19xNSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pID4gVERfTWlfU1NIR19xOTUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTSEdfQ250X0YgPCAyMCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTX0NudF9GID49IDIwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfU1NfTWVhbl9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoIWlzLm5hKFRyYXZlbERpc3RhbmNlX01pKSAmIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoVHJhdmVsRGlzdGFuY2VfTWkgPCBURF9NaV9TU0hHX3E1IHwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWkgPiBURF9NaV9TU0hHX3E5NQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfU1NfQ250X0YgPCAyMCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTX0NudCA+PSAyMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTX01lYW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSkgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBCdXNEYXlfRXZlbnROdW0gIT0gMSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX0h2cnMgIT0gMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX0h2cnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSkgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBCdXNEYXlfRXZlbnROdW0gIT0gMSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX0h2cnMgPT0gMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTX01lYW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApKSkpKSwKICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfTmV3X0xhYmVsID0gCiAgICAgICAgICAgZmFjdG9yKGlmZWxzZSghaXMubmEoVHJhdmVsRGlzdGFuY2VfTWkpICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgKFRyYXZlbERpc3RhbmNlX01pIDwgVERfTWlfU1NIR19xNSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pID4gVERfTWlfU1NIR19xOTUKICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFREX01pX1NTSEdfQ250X0YgPj0gMjAsCiAgICAgICAgICAgICAgICAgICAgICAgICAiVERfTWlfU1NIR19NZWFuX0YiLAogICAgICAgICAgICAgICAgICBpZmVsc2UoIWlzLm5hKFRyYXZlbERpc3RhbmNlX01pKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgIChUcmF2ZWxEaXN0YW5jZV9NaSA8IFREX01pX1NTSEdfcTUgfAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaSA+IFREX01pX1NTSEdfcTk1CiAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJgogICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU0hHX0NudF9GIDwgMjAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU19DbnRfRiA+PSAyMCwKICAgICAgICAgICAgICAgICAgICAgICAgICJURF9NaV9TU19NZWFuX0YiLAogICAgICAgICAgICAgICAgICBpZmVsc2UoIWlzLm5hKFRyYXZlbERpc3RhbmNlX01pKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgIChUcmF2ZWxEaXN0YW5jZV9NaSA8IFREX01pX1NTSEdfcTUgfAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaSA+IFREX01pX1NTSEdfcTk1CiAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJgogICAgICAgICAgICAgICAgICAgICAgICAgICBURF9NaV9TU19DbnRfRiA8IDIwICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgVERfTWlfU1NfQ250ID49IDIwLAogICAgICAgICAgICAgICAgICAgICAgICAgIlREX01pX1NTX01lYW4iLAogICAgICAgICAgICAgICAgICBpZmVsc2UoaXMubmEoVHJhdmVsRGlzdGFuY2VfTWkpICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgQnVzRGF5X0V2ZW50TnVtICE9IDEgJgogICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzICE9IDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAiVHJhdmVsRGlzdGFuY2VfTWlfSHZycyIsCiAgICAgICAgICAgICAgICAgIGlmZWxzZShpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSkgJgogICAgICAgICAgICAgICAgICAgICAgICAgICBCdXNEYXlfRXZlbnROdW0gIT0gMSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX0h2cnMgPT0gMCwKICAgICAgICAgICAgICAgICAgICAgICAgICJURF9NaV9TU19NZWFuIiwKICAgICAgICAgICAgICAgICAgICAgICAgICJUcmF2ZWxEaXN0YW5jZV9NaSIKICAgICAgICAgICAgICAgICAgICAgICAgKSkpKSkKICAgICAgICAgICAgICAgICApLAogICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzID0gaWZlbHNlKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzICE9IDAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKFRyYXZlbERpc3RhbmNlX01pX05ldyA8IFREX01pX3EyIHwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ldyA+IFREX01pX3E5OAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ldwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZyc19MYWJlbCA9CiAgICAgICAgICAgZmFjdG9yKGlmZWxzZSghaXMubmEoVHJhdmVsRGlzdGFuY2VfTWlfSHZycykgJgogICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzICE9IDAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAoVHJhdmVsRGlzdGFuY2VfTWlfTmV3IDwgVERfTWlfcTIgfAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcgPiBURF9NaV9xOTgKICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICJUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzIiwKICAgICAgICAgICAgICAgICAgICAgICAgIGFzLmNoYXJhY3RlcihUcmF2ZWxEaXN0YW5jZV9NaV9OZXdfTGFiZWwpCiAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICApLAogICAgICAgICBTcGVlZEF2Z19NcGhfTmV3SHZycyA9IFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMgLyBUcmF2ZWxUaW1lX0hyCiAgICAgICAgKQoKcm0oQWxsRGF5c19OZXdPcmRlcikKc3RyKEFsbERheXNfTmV3VHJhdmVsRGlzdCkKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgVHJhdmVsRGlzdGFuY2VfTWkgJiBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzICYgVHJhdmVsRGlzdGFuY2VfTWlfTmV3LgoKUXVpY2sgc3VtbWFyeSBhbmQgdGhlbiBjb3JyZWxhdGlvbiBjYWxjdWxhdGlvbi4KYGBge3J9CgojIDM4IHJvd3MgbWVldCB0aGlzIGNyaXRlcmlhIGFueW1vcmUgIC0tICBhcHBlYXJzIHRvIGJlIHRoZSBjYXNlIHdoZW4gYm90aCB0aGUgTGF0IExvbmcgY2FsY3VsYXRpb25zLCBhbmQgdGhlIFRyYXZlbERpc3RhbmNlIGNhbGN1bGF0aW9ucyBkaWQgbm90IGZ1bmN0aW9uIHByb3Blcmx5LgpWaWV3KGZpbHRlcihBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgIGlzLm5hKFRyYXZlbERpc3RhbmNlX01pX05ldykgJgogICAgICAgICAgICAgIEJ1c0RheV9FdmVudE51bSAhPSAxCiAgICAgICAgICAgKQogICAgKQoKVmlldyhBbGxEYXlzX05ld1RyYXZlbERpc3QgJT4lIAogICAgICAgYXJyYW5nZShkZXNjKFRyYXZlbERpc3RhbmNlX01pX05ldykpICU+JSAKICAgICAgIGhlYWQoNTAwKQogICAgKQoKc3VtbWFyeShzZWxlY3QoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaSwKICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfSHZycywKICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfTmV3LAogICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzCiAgICAgICAgICAgICAgKQogICAgICAgKQoKc3VtbWFyeShzZWxlY3QoZmlsdGVyKEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgICAgICAgICAgIEJ1c0RheV9FdmVudE51bSAhPSAxCiAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pLAogICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzLAogICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcsCiAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMKICAgICAgICAgICAgICApCiAgICAgICApCgoKY29yKHNlbGVjdChBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWksCiAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfSHZycywKICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcsCiAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycwogICAgICAgICAgKSwKICAgIHVzZSA9ICJwYWlyd2lzZS5jb21wbGV0ZS5vYnMiCiAgKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzX0xhYmVsICYgVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZyc19MYWJlbC4KClNob3cgaG93IHRoZSBsYWJlbHMgY2hhbmdlZC4KYGBge3J9Cgpncm91cF9ieShBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ld19MYWJlbCwKICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZyc19MYWJlbAogICAgICAgICkgJT4lIAogIHN1bW1hcmlzZShDbnROdW0gPSBuKCksCiAgICAgICAgICAgIENudFBjdCA9IGZvcm1hdChDbnROdW0gLyBucm93KEFsbERheXNfTmV3VHJhdmVsRGlzdCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzY2llbnRpZmljID0gOTk5OQogICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgKSAlPiUgCiAgYXJyYW5nZShkZXNjKENudFBjdCkKICAgICAgICAgKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxEaXN0YW5jZV9NaSAmIFRyYXZlbERpc3RhbmNlX01pX0h2cnMgJiBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcuCgpHcmFwaGluZyB0aGUgdHdvIG1ldGhvZHMgb2YgY2FsY3VsYXRpbmcgVHJhdmVsRGlzdGFuY2VfTWkuCgpGaXJzdCwgbGV0J3MgZ2V0IGNyZWF0ZSBhIGZ1bmN0aW9uIHRvIHBsb3QgdGhlIGxpbmVyIG1vZGVsIGVxdWF0aW9uLgpgYGB7cn0KCmxtX2VxbiA8LSBmdW5jdGlvbihkZiwgeSwgeCl7CiAgbSA8LSBsbSh5IH4geCwgZGYpCiAgCiAgbCA8LSBsaXN0KGEgPSBmb3JtYXQoY29lZihtKVsxXSwgZGlnaXRzID0gMiksCiAgICAgICAgICAgIGIgPSBmb3JtYXQoYWJzKGNvZWYobSlbMl0pLCBkaWdpdHMgPSAyKSwKICAgICAgICAgICAgczEgPSBpZmVsc2UodGVzdCA9IGNvZWYobSlbMl0gPiAwLAogICAgICAgICAgICAgICAgICAgICAgICB5ZXMgPSAiKyIsCiAgICAgICAgICAgICAgICAgICAgICAgIG5vID0gIi0iCiAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgcjIgPSBmb3JtYXQoc3VtbWFyeShtKSRyLnNxdWFyZWQsCiAgICAgICAgICAgICAgICAgICAgICAgIGRpZ2l0cyA9IDMKICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgKQogIAogIGVxIDwtIHN1YnN0aXR1dGUoaXRhbGljKHkpID09IGF+fnMxfn5iICUuJSBpdGFsaWMoeCkqIiwifn5pdGFsaWMocileMn4iPSJ+cjIsCiAgICAgICAgICAgICAgICAgICBsCiAgICAgICAgICAgICAgICAgICkKICAKICBhcy5jaGFyYWN0ZXIoYXMuZXhwcmVzc2lvbihlcSkKICAgICAgICAgICAgICApICAgICAgICAgICAgIAp9CgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFRyYXZlbERpc3RhbmNlX01pICYgVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycy4KClNjYXR0ZXIgcGxvdCAodXNpbmcgYSAxMCUgc2FtcGxlIHRvIG1ha2luZyBwbG90dGluZyB0aW1lIGZhc3RlciBhbmQgdG8gcmVkdWNlIHVuLW5lZWRlZCBkYXRhIGluIHRoZSAic2FtZSIgc3Bsb3QpLgpgYGB7cn0KCnNldC5zZWVkKDEyMzQ1Njc4OSkKQWxsRGF5c19OZXdUcmF2ZWxEaXN0XzEwUGN0IDwtIGZpbHRlcihBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIWlzLm5hKFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMpICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHJlbmFtZShEaXN0TWV0aG9kID0gVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZyc19MYWJlbCkgJT4lIAogIHNhbXBsZV9mcmFjKDAuMSkKCgpUcmF2RGlzdF9NaVZzQ2FsYyA8LSBnZ3Bsb3Qoc2VsZWN0KEFsbERheXNfTmV3VHJhdmVsRGlzdF8xMFBjdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIERpc3RNZXRob2QKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IFRyYXZlbERpc3RhbmNlX01pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG91ciA9IERpc3RNZXRob2QKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgKSArCiAgc2NhbGVfY29sb3VyX21hbnVhbCh2YWx1ZXMgPSBjKCJyZWQiLCJibHVlIiwgImdyZWVuIiwgIm9yYW5nZSIsICJibGFjayIpCiAgICAgICAgICAgICAgICAgICAgICkgKwogIGdlb21fcG9pbnQoc2hhcGUgPSAxLCBhbHBoYSA9IDAuNSkgKwogIHNjYWxlX3NoYXBlKHNvbGlkID0gRkFMU0UpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBjb2xvdXIgPSAiYmx1ZSIpICsKICBnZW9tX2FibGluZShpbnRlcmNlcHQgPSAwLCBzbG9wZSA9IDEsIGNvbG91ciA9ICJyZWQiKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDAsIDEuNSksIHlsaW0gPSBjKDAsIDEuNSkKICAgICAgICAgICAgICAgICApICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEuNSwgMC4yNSkKICAgICAgICAgICAgICAgICAgICApICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDEuNSwgMC4yNSkKICAgICAgICAgICAgICAgICAgICApICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwgI2MoMC44NSwgMC40MCksCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDYpCiAgICAgICApICsKICBhbm5vdGF0ZShsYWJlbCA9IGxtX2VxbihkZiA9IEFsbERheXNfTmV3VHJhdmVsRGlzdF8xMFBjdCwKICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gQWxsRGF5c19OZXdUcmF2ZWxEaXN0XzEwUGN0JFRyYXZlbERpc3RhbmNlX01pLAogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBBbGxEYXlzX05ld1RyYXZlbERpc3RfMTBQY3QkVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycwogICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAjIHggPSA2MiwKICAgICAgICAgICAjIHkgPSAyMCwKICAgICAgICAgICB4ID0gMC43MCwKICAgICAgICAgICB5ID0gMC4wMCwKICAgICAgICAgICBnZW9tID0gInRleHQiLAogICAgICAgICAgIHNpemUgPSAzLAogICAgICAgICAgIGNvbG91ciA9ICJibHVlIiwKICAgICAgICAgICBwYXJzZSA9IFRSVUUKICAgICAgICAgICkgKwogIGFubm90YXRlKGxhYmVsID0gIlJlZmVyZW5jZSBMaW5lIChzbG9wZSA9IDEpIiwKICAgICAgICAgICAjIHggPSAxNiwKICAgICAgICAgICAjIHkgPSAzMCwKICAgICAgICAgICB4ID0gMC44MCwKICAgICAgICAgICB5ID0gMS4wNSwKICAgICAgICAgICBnZW9tID0gInRleHQiLAogICAgICAgICAgIHNpemUgPSAzLAogICAgICAgICAgIGNvbG91ciA9ICJyZWQiCiAgICAgICAgICApICsKICBsYWJzKHRpdGxlID0gIlRyYXZlbERpc3RhbmNlX01pIHZzLiBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzIiwKICAgICAgIHggPSAiVHJhdmVsRGlzdGFuY2VfTWkiLAogICAgICAgeSA9ICJUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzIgogICAgICApCiMgKwojICAgZ2VvbV9qaXR0ZXIoKQoKVHJhdkRpc3RfTWlWc0NhbGMKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgVHJhdmVsRGlzdGFuY2VfTWkgJiBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzICYgVHJhdmVsRGlzdGFuY2VfTWlfTmV3LgoKR3JhcGhpbmcgdGVzdCB3aXRoIHJib2tlaC4KYGBge3J9CgpUcmF2RGlzdF9NaVZzQ2FsY19Cb2tlaCA8LSBmaWd1cmUoZGF0YSA9IHNlbGVjdChBbGxEYXlzX05ld1RyYXZlbERpc3RfMTBQY3QsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBEaXN0TWV0aG9kCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHhsaW0gPSBjKDAsIDEuNSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5bGltID0gYygwLCAxLjUpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kX2xvY2F0aW9uID0gImJvdHRvbV9yaWdodCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbHlfcG9pbnRzKHggPSBUcmF2ZWxEaXN0YW5jZV9NaSwKICAgICAgICAgICAgeSA9IFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMsCiAgICAgICAgICAgIGNvbG9yID0gRGlzdE1ldGhvZCwKICAgICAgICAgICAgaG92ZXIgPSBjKFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMsIFRyYXZlbERpc3RhbmNlX01pLCBEaXN0TWV0aG9kKQogICAgICAgICAgICkgJT4lIAogIGx5X2FibGluZShhID0gMCwgYiA9IDEsIGNvbG9yID0gInJlZCIpCgpUcmF2RGlzdF9NaVZzQ2FsY19Cb2tlaAoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcuCgpDYWxjdWxhdGluZyB0aGUgbWluaW11bSBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcgdmFsdWUgYXQgZWFjaCBwZXJjZW50aWxlLgpgYGB7cn0KCnJtKFRyYXZEaXN0X01pVnNDYWxjX0Jva2VoKQpybShBbGxEYXlzX05ld1RyYXZlbERpc3RfMTBQY3QpCgoKc3VtbWFyeShzZWxlY3QoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaSwKICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfSHZycywKICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfTmV3LAogICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzCiAgICAgICAgICAgICAgKQogICAgICAgKQoKc3VtbWFyeShzZWxlY3QoZmlsdGVyKEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgICAgICAgICAgIEJ1c0RheV9FdmVudE51bSAhPSAxCiAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pLAogICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9IdnJzLAogICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcsCiAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMKICAgICAgICAgICAgICApCiAgICAgICApCgoKVHJhdkRpc3RNaU5fTnRpbGUgPC0gYXMuZGF0YS5mcmFtZShzZWxlY3QoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTdGFydFN0b3BfSUQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ld19MYWJlbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzX0xhYmVsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKFBjdFJfTiA9IHBlcmNlbnRfcmFuayhBbGxEYXlzX05ld1RyYXZlbERpc3QkVHJhdmVsRGlzdGFuY2VfTWlfTmV3KSwKICAgICAgICAgIyBQY3RSX0ggPSBwZXJjZW50X3JhbmsoQWxsRGF5c19OZXdUcmF2ZWxEaXN0JFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMpLAogICAgICAgICBQY3RSX1JvdW5kX04gPSByb3VuZChQY3RSX04sIDIpCiAgICAgICAgICMgUGN0Ul9Sb3VuZF9IID0gcm91bmQoUGN0Ul9ILCAyKQogICAgICAgICkgCgojIHN0cihUcmF2RGlzdE1pTl9OdGlsZSkKIyBWaWV3KGhlYWQoVHJhdkRpc3RNaU5fTnRpbGUsIDUwMCkpCgpUcmF2RGlzdE1pTl9OdGlsZV9Sb3dzIDwtIG5yb3coVHJhdkRpc3RNaU5fTnRpbGUpCgojIFZpZXcodGFpbChUcmF2RGlzdE1pTl9OdGlsZSwgNTAwKSkKCgpUcmF2RGlzdE1pTl9QY3RpbGVzIDwtIGdyb3VwX2J5KFRyYXZEaXN0TWlOX050aWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBjdFJfUm91bmRfTgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgc3VtbWFyaXNlKAogICAgTWluVERNaUF0UGN0aWxlX04gPSBtaW4oVHJhdmVsRGlzdGFuY2VfTWlfTmV3KSwKICAgICMgTWluVERNaUF0UGN0aWxlX0ggPSBtaW4oVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycyksCiAgICBDbnRzQXRQY3RpbGVfTiA9IHN1bSghaXMubmEoVHJhdmVsRGlzdGFuY2VfTWlfTmV3KSksCiAgICAjIENudHNBdFBjdGlsZV9IID0gc3VtKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzKSksCiAgICBQY3RzQXRQY3RpbGVfTiA9IENudHNBdFBjdGlsZV9OIC8gVHJhdkRpc3RNaU5fTnRpbGVfUm93cwogICAgIyBQY3RzQXRQY3RpbGVfSCA9IENudHNBdFBjdGlsZV9IIC8gVHJhdkRpc3RNaU5fTnRpbGVfUm93cwogICkgJT4lIAogIG11dGF0ZShDdW1TdW1QQXRQX04gPSBjdW1zdW0oUGN0c0F0UGN0aWxlX04pCiAgICAgICAgICMgQ3VtU3VtUEF0UF9IID0gY3Vtc3VtKFBjdHNBdFBjdGlsZV9IKQogICAgICAgICkKCiMgVmlldyhUcmF2RGlzdE1pTl9QY3RpbGVzKQpUcmF2RGlzdE1pTl9QY3RpbGVzCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMKCkNhbGN1bGF0aW5nIHRoZSBtaW5pbXVtIFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMgdmFsdWUgYXQgZWFjaCBwZXJjZW50aWxlLgpgYGB7cn0KClRyYXZEaXN0TWlIX050aWxlIDwtIGFzLmRhdGEuZnJhbWUoc2VsZWN0KEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RhcnRTdG9wX0lELAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFRyYXZlbERpc3RhbmNlX01pX05ld19MYWJlbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZyc19MYWJlbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBtdXRhdGUoIyBQY3RSX04gPSBwZXJjZW50X3JhbmsoQWxsRGF5c19OZXdUcmF2ZWxEaXN0JFRyYXZlbERpc3RhbmNlX01pX05ldyksCiAgICAgICAgIFBjdFJfSCA9IHBlcmNlbnRfcmFuayhBbGxEYXlzX05ld1RyYXZlbERpc3QkVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycyksCiAgICAgICAgICMgUGN0Ul9Sb3VuZF9OID0gcm91bmQoUGN0Ul9OLCAyKSwKICAgICAgICAgUGN0Ul9Sb3VuZF9IID0gcm91bmQoUGN0Ul9ILCAyKQogICAgICAgICkgCgojIHN0cihUcmF2RGlzdE1pSF9OdGlsZSkKIyBWaWV3KGhlYWQoVHJhdkRpc3RNaUhfTnRpbGUsIDUwMCkpCgpUcmF2RGlzdE1pSF9OdGlsZV9Sb3dzIDwtIG5yb3coVHJhdkRpc3RNaUhfTnRpbGUpCgojIFZpZXcodGFpbChUcmF2RGlzdE1pSF9OdGlsZSwgNTAwKSkKCgpUcmF2RGlzdE1pSF9QY3RpbGVzIDwtIGdyb3VwX2J5KFRyYXZEaXN0TWlIX050aWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBjdFJfUm91bmRfSAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgc3VtbWFyaXNlKAogICAgIyBNaW5URE1pQXRQY3RpbGVfTiA9IG1pbihUcmF2ZWxEaXN0YW5jZV9NaV9OZXcpLAogICAgTWluVERNaUF0UGN0aWxlX0ggPSBtaW4oVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycyksCiAgICAjIENudHNBdFBjdGlsZV9OID0gc3VtKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaV9OZXcpKSwKICAgIENudHNBdFBjdGlsZV9IID0gc3VtKCFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzKSksCiAgICAjIFBjdHNBdFBjdGlsZV9OID0gQ250c0F0UGN0aWxlX04gLyBUcmF2RGlzdE1pSF9OdGlsZV9Sb3dzLAogICAgUGN0c0F0UGN0aWxlX0ggPSBDbnRzQXRQY3RpbGVfSCAvIFRyYXZEaXN0TWlIX050aWxlX1Jvd3MKICApICU+JSAKICBtdXRhdGUoIyBDdW1TdW1QQXRQX04gPSBjdW1zdW0oUGN0c0F0UGN0aWxlX04pLAogICAgICAgICBDdW1TdW1QQXRQX0ggPSBjdW1zdW0oUGN0c0F0UGN0aWxlX0gpCiAgICAgICAgKQoKIyBWaWV3KFRyYXZEaXN0TWlIX1BjdGlsZXMpClRyYXZEaXN0TWlIX1BjdGlsZXMKCmBgYAoKCkpvaW4gVHJhdkRpc3RNaUhfUGN0aWxlcywgVHJhdkRpc3RNaU5fUGN0aWxlcywgYW5kIFRyYXZEaXN0TWlfUGN0aWxlcy4KCn4xMSUgb2YgcmlkZXMgYXJlIHN0aWxsIHNob3dpbmcgYXMgbGVzcyB0aGFuIDAuMSBtaWxlcyBvZiBUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzLgpgYGB7cn0KCnJtKFRyYXZEaXN0TWlOX050aWxlX1Jvd3MsIFRyYXZEaXN0TWlIX050aWxlX1Jvd3MsIFRyYXZEaXN0TWlOX050aWxlLCBUcmF2RGlzdE1pSF9OdGlsZSkKCgojIFZpZXcoVHJhdkRpc3RNaV9QY3RpbGVzKQojIFZpZXcoVHJhdkRpc3RNaU5fUGN0aWxlcykKIyBWaWV3KFRyYXZEaXN0TWlIX1BjdGlsZXMpCgpUcmF2RGlzdE1pX1BjdGlsZXNfQWxsIDwtIGlubmVyX2pvaW4oeCA9IFRyYXZEaXN0TWlfUGN0aWxlcywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBUcmF2RGlzdE1pTl9QY3RpbGVzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSBjKCJQY3RSX1JvdW5kIiA9ICJQY3RSX1JvdW5kX04iKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBpbm5lcl9qb2luKHkgPSBUcmF2RGlzdE1pSF9QY3RpbGVzLAogICAgICAgICAgICAgYnkgPSBjKCJQY3RSX1JvdW5kIiA9ICJQY3RSX1JvdW5kX0giKQogICAgICAgICAgICApICU+JSAKICBzZWxlY3QoUGN0Ul9Sb3VuZCwKICAgICAgICAgTWluVHJhdkRpc3RNaUF0UGN0aWxlLAogICAgICAgICBNaW5URE1pQXRQY3RpbGVfTiwKICAgICAgICAgTWluVERNaUF0UGN0aWxlX0gsCiAgICAgICAgIENudHNBdFBjdGlsZSwKICAgICAgICAgQ250c0F0UGN0aWxlX04sCiAgICAgICAgIENudHNBdFBjdGlsZV9ILAogICAgICAgICBQY3RzQXRQY3RpbGUsCiAgICAgICAgIFBjdHNBdFBjdGlsZV9OLAogICAgICAgICBQY3RzQXRQY3RpbGVfSCwKICAgICAgICAgQ3VtU3VtUEF0UCwKICAgICAgICAgQ3VtU3VtUEF0UF9OLAogICAgICAgICBDdW1TdW1QQXRQX0gKICAgICAgICAgKQoKIyBzdHIoVHJhdkRpc3RNaV9QY3RpbGVzX0FsbCkKCnJtKFRyYXZEaXN0TWlfUGN0aWxlcywgVHJhdkRpc3RNaU5fUGN0aWxlcyxUcmF2RGlzdE1pSF9QY3RpbGVzKQoKClZpZXcoVHJhdkRpc3RNaV9QY3RpbGVzX0FsbCkKVHJhdkRpc3RNaV9QY3RpbGVzX0FsbAoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcuCgpXaHkgYXJlIHRoZXJlIHN0aWxsIHNvbWUgc21hbGwgb3IgbGFyZ2UgVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycyB2YWx1ZXMuCmBgYHtyfQoKIyBWaWV3KGZpbHRlcihBbGxEYXlzX05ld1RyYXZlbERpc3QsCiMgICAgICAgICAgICAgIWlzLm5hKFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMpCiMgICAgICAgICAgICApICU+JSAKIyAgICAgICAgc2VsZWN0KC1URF9NaV9xMiwKIyAgICAgICAgICAgICAgIC1URF9NaV9xOTgsCiMgICAgICAgICAgICAgICAtVERfTWlfU1NfcTUsCiMgICAgICAgICAgICAgICAtVERfTWlfU1NfcTk1LAojICAgICAgICAgICAgICAgLVREX01pX1NTSEdfcTUsCiMgICAgICAgICAgICAgICAtVERfTWlfU1NIR19xOTUsCiMgICAgICAgICAgICAgICAtVERfTWlfTWVhbiwKIyAgICAgICAgICAgICAgIC1URF9NaV9NZWFuX0YsCiMgICAgICAgICAgICAgICAtVERfTWlfU1NfTWVhbiwKIyAgICAgICAgICAgICAgIC1URF9NaV9TU19NZWFuX0YsCiMgICAgICAgICAgICAgICAtVERfTWlfU1NIR19NZWFuLAojICAgICAgICAgICAgICAgLVREX01pX1NTSEdfTWVhbl9GLAojICAgICAgICAgICAgICAgLVREX01pX01lZCwKIyAgICAgICAgICAgICAgIC1URF9NaV9NZWRfRiwKIyAgICAgICAgICAgICAgIC1URF9NaV9TU19NZWQsCiMgICAgICAgICAgICAgICAtVERfTWlfU1NfTWVkX0YsCiMgICAgICAgICAgICAgICAtVERfTWlfU1NIR19NZWQsCiMgICAgICAgICAgICAgICAtVERfTWlfU1NIR19NZWRfRiwKIyAgICAgICAgICAgICAgIC1URF9NaV9DbnQsCiMgICAgICAgICAgICAgICAtVERfTWlfQ250X0YsCiMgICAgICAgICAgICAgICAtVERfTWlfU1NfQ250LAojICAgICAgICAgICAgICAgLVREX01pX1NTX0NudF9GLAojICAgICAgICAgICAgICAgLVREX01pX1NTSEdfQ250LAojICAgICAgICAgICAgICAgLVREX01pX1NTSEdfQ250X0YsCiMgICAgICAgICAgICAgICAtVFRfU2VjX3EyLAojICAgICAgICAgICAgICAgLVRUX1NlY19xOTgsCiMgICAgICAgICAgICAgICAtVFRfU2VjX1NTX3E1LAojICAgICAgICAgICAgICAgLVRUX1NlY19TU19xOTUsCiMgICAgICAgICAgICAgICAtVFRfU2VjX1NTSEdfcTUsCiMgICAgICAgICAgICAgICAtVFRfU2VjX1NTSEdfcTk1LAojICAgICAgICAgICAgICAgLVRUX1NlY19NZWFuLAojICAgICAgICAgICAgICAgLVRUX1NlY19NZWFuX0YsCiMgICAgICAgICAgICAgICAtVFRfU2VjX1NTX01lYW4sCiMgICAgICAgICAgICAgICAtVFRfU2VjX1NTX01lYW5fRiwKIyAgICAgICAgICAgICAgIC1UVF9TZWNfU1NIR19NZWFuLAojICAgICAgICAgICAgICAgLVRUX1NlY19TU0hHX01lYW5fRiwKIyAgICAgICAgICAgICAgIC1UVF9TZWNfTWVkLAojICAgICAgICAgICAgICAgLVRUX1NlY19NZWRfRiwKIyAgICAgICAgICAgICAgIC1UVF9TZWNfU1NfTWVkLAojICAgICAgICAgICAgICAgLVRUX1NlY19TU19NZWRfRiwKIyAgICAgICAgICAgICAgIC1UVF9TZWNfU1NIR19NZWQsCiMgICAgICAgICAgICAgICAtVFRfU2VjX1NTSEdfTWVkX0YsCiMgICAgICAgICAgICAgICAtVFRfU2VjX0NudCwKIyAgICAgICAgICAgICAgIC1UVF9TZWNfQ250X0YsCiMgICAgICAgICAgICAgICAtVFRfU2VjX1NTX0NudCwKIyAgICAgICAgICAgICAgIC1UVF9TZWNfU1NfQ250X0YsCiMgICAgICAgICAgICAgICAtVFRfU2VjX1NTSEdfQ250LAojICAgICAgICAgICAgICAgLVRUX1NlY19TU0hHX0NudF9GLAojICAgICAgICAgICAgICAgLVRUX0hyX3EyLAojICAgICAgICAgICAgICAgLVRUX0hyX3E5OCwKIyAgICAgICAgICAgICAgIC1UVF9Icl9TU19xNSwKIyAgICAgICAgICAgICAgIC1UVF9Icl9TU19xOTUsCiMgICAgICAgICAgICAgICAtVFRfSHJfU1NIR19xNSwKIyAgICAgICAgICAgICAgIC1UVF9Icl9TU0hHX3E5NSwKIyAgICAgICAgICAgICAgIC1UVF9Icl9NZWFuLAojICAgICAgICAgICAgICAgLVRUX0hyX01lYW5fRiwKIyAgICAgICAgICAgICAgIC1UVF9Icl9TU19NZWFuLAojICAgICAgICAgICAgICAgLVRUX0hyX1NTX01lYW5fRiwKIyAgICAgICAgICAgICAgIC1UVF9Icl9TU0hHX01lYW4sCiMgICAgICAgICAgICAgICAtVFRfSHJfU1NIR19NZWFuX0YsCiMgICAgICAgICAgICAgICAtVFRfSHJfTWVkLAojICAgICAgICAgICAgICAgLVRUX0hyX01lZF9GLAojICAgICAgICAgICAgICAgLVRUX0hyX1NTX01lZCwKIyAgICAgICAgICAgICAgIC1UVF9Icl9TU19NZWRfRiwKIyAgICAgICAgICAgICAgIC1UVF9Icl9TU0hHX01lZCwKIyAgICAgICAgICAgICAgIC1UVF9Icl9TU0hHX01lZF9GLAojICAgICAgICAgICAgICAgLVRUX0hyX0NudCwKIyAgICAgICAgICAgICAgIC1UVF9Icl9DbnRfRiwKIyAgICAgICAgICAgICAgIC1UVF9Icl9TU19DbnQsCiMgICAgICAgICAgICAgICAtVFRfSHJfU1NfQ250X0YsCiMgICAgICAgICAgICAgICAtVFRfSHJfU1NIR19DbnQsCiMgICAgICAgICAgICAgICAtVFRfSHJfU1NIR19DbnRfRgojICAgICAgICAgICAgICApICU+JSAKIyAgICAgICAgYXJyYW5nZShUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzKSAlPiUgCiMgICAgICAgIGhlYWQoNTAwKQojICAgICApCgpWaWV3KGZpbHRlcihBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgICFpcy5uYShUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzKQogICAgICAgICAgICkgJT4lIAogICAgICAgc2VsZWN0KC1tYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICAgICkgJT4lIAogICAgICAgYXJyYW5nZShUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzKSAlPiUKICAgICAgIGhlYWQoNTAwKQogICAgKQoKIyBleGFtcGxlcyBvZiB0aGUgc21hbGxlc3QgVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycyB2YWx1ZXMuClZpZXcoZmlsdGVyKEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgKFJvd051bV9PRyA+PSAxNDI0NDQwICYgUm93TnVtX09HIDw9IDE0MjQ0NjApIHwgIyAxNDI0NDUwICAtLSAgZGlyZWN0aW9uIGNoYW5nZQogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSA3NjMyOTIgJiBSb3dOdW1fT0cgPD0gNzYzMzEyKSB8ICMgNzYzMzAyICAtLSAgZGlyZWN0aW9uIGNoYW5nZQogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAxNjc5MDkzICYgUm93TnVtX09HIDw9IDE2NzkxMTMpIHwgIyAxNjc5MTAzICAtLSAgZGlyZWN0aW9uIGNoYW5nZQogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAyODYwOTE4ICYgUm93TnVtX09HIDw9IDI4NjA5MzgpICMgMjg2MDkyOCAgLS0gIGxvb2tzIGNvcnJlY3QKICAgICAgICAgICApICU+JSAKICAgICAgIHNlbGVjdCgtbWF0Y2hlcygiKHEoMnw1fCg5NSl8KDk4KSkpfE1lYW58TWVkfENudCIpCiAgICAgICAgICAgICApCiAgICApCgoKVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAhaXMubmEoVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycykKICAgICAgICAgICApICU+JSAKICAgICAgIHNlbGVjdCgtbWF0Y2hlcygiKHEoMnw1fCg5NSl8KDk4KSkpfE1lYW58TWVkfENudCIpCiAgICAgICAgICAgICApICU+JSAKICAgICAgIGFycmFuZ2UoZGVzYyhUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzKQogICAgICAgICAgICAgICkgJT4lCiAgICAgICBoZWFkKDUwMCkKICAgICkKCiMgZXhhbXBsZXMgb2YgdGhlIGxhcmdlc3QgVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycyB2YWx1ZXMuClZpZXcoZmlsdGVyKEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgKFJvd051bV9PRyA+PSAxMDkyMDAwICYgUm93TnVtX09HIDw9IDEwOTIwNTApIHwgIyAxMDkyMDMwICAtLSAgZGlyZWN0aW9uIGNoYW5nZQogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAxNjA5NDYwICYgUm93TnVtX09HIDw9IDE2MDk0ODApIHwgIyAxNjA5NDcwICAtLSBkaXJlY3Rpb24gY2hhbmdlIAogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSA1MDg5MDQgJiBSb3dOdW1fT0cgPD0gNTA4OTI0KSB8ICMgNTA4OTE0ICAtLSAgZGlyZWN0aW9uIGNoYW5nZSAmIG9yaWdpbmFsIFN0b3BJRCB3YXMgYmFkCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDI0NzYzNDUgJiBSb3dOdW1fT0cgPD0gMjQ3NjM2NSkgIyAyNDc2MzU1ICAtLSAgZGlyZWN0aW9uIGNoYW5nZQogICAgICAgICAgICkgJT4lIAogICAgICAgc2VsZWN0KC1tYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICAgICkKICAgICkKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgVHJhdmVsVGltZV9Ici4KClZpZXcoVHJhdkRpc3RNaV9QY3RpbGVzKTogOTglIG9mIFRyYXZlbFRpbWVfSHIgYXJlIGJldHdlZW4gNyBzZWNvbmRzIGFuZCA0NjQgc2Vjb25kcyAofjggbWludXRlcykuCmBgYHtyfQoKVHJhdlRpbWVIcl9OdGlsZSA8LSBzZWxlY3QoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxUaW1lX0hyCiAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKCMgUGN0aWxlID0gbnRpbGUoQWxsRGF5c19OZXdUcmF2ZWxEaXN0JFRyYXZlbFRpbWVfSHIsIDEwMCksCiAgICAgICAgICMgTWluUiA9IG1pbl9yYW5rKEFsbERheXNfTmV3VHJhdmVsRGlzdCRUcmF2ZWxUaW1lX0hyKSwKICAgICAgICAgUGN0UiA9IHBlcmNlbnRfcmFuayhBbGxEYXlzX05ld1RyYXZlbERpc3QkVHJhdmVsVGltZV9IciksCiAgICAgICAgIFBjdFJfUm91bmQgPSByb3VuZChQY3RSLCAyKQogICAgICAgICkgCgojIHN0cihUcmF2VGltZUhyX050aWxlKQoKVHJhdlRpbWVIcl9OdGlsZV9Sb3dzIDwtIG5yb3coVHJhdlRpbWVIcl9OdGlsZSkKCiMgVmlldyh0YWlsKFRyYXZUaW1lSHJfTnRpbGUsIDUwMCkpCgoKVHJhdlRpbWVIcl9QY3RpbGVzIDwtIGdyb3VwX2J5KFRyYXZUaW1lSHJfTnRpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQY3RSX1JvdW5kCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHN1bW1hcmlzZSgKICAgIE1pblRyYXZUaW1lSHJBdFBjdGlsZSA9IG1pbihUcmF2ZWxUaW1lX0hyKSwKICAgIENudHNBdFBjdGlsZSA9IG4oKSwKICAgIFBjdHNBdFBjdGlsZSA9IENudHNBdFBjdGlsZSAvIFRyYXZUaW1lSHJfTnRpbGVfUm93cwogICkgJT4lIAogIG11dGF0ZShDdW1TdW1QQXRQID0gY3Vtc3VtKFBjdHNBdFBjdGlsZSksCiAgICAgICAgIE1pblRyYXZUaW1lU2VjQXRQY3RpbGUgPSBNaW5UcmF2VGltZUhyQXRQY3RpbGUgKiAzNjAwCiAgICAgICAgKQoKcm0oVHJhdlRpbWVIcl9OdGlsZV9Sb3dzKQpybShUcmF2VGltZUhyX050aWxlKQpWaWV3KFRyYXZUaW1lSHJfUGN0aWxlcykKVHJhdlRpbWVIcl9QY3RpbGVzCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFRyYXZlbFRpbWVfSHIuCgpIaXN0b2dyYW0gb2YgVHJhdmVsVGltZV9TZWMuCmBgYHtyfQoKVHJhdlRpbWVfU2VjX0hpc3REZW4gPC0gZ2dwbG90KGZpbHRlcihzZWxlY3QoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxUaW1lX1NlYwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIWlzLm5hKFRyYXZlbFRpbWVfU2VjKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gVHJhdmVsVGltZV9TZWMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IC4uZGVuc2l0eS4uCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSA1LCBmaWxsID0gImxpZ2h0Ymx1ZSIsIGNvbG91ciA9ICJncmV5NjAiLCBzaXplID0gMC4yKSArCiAgZ2VvbV9saW5lKHN0YXQgPSAiZGVuc2l0eSIsIGNvbG91ciA9ICJyZWQiKSArCiAgIyBzdGF0X2JpbihiaW53aWR0aCA9IDUsCiAgIyAgICAgICAgICBnZW9tID0gInRleHQiLAogICMgICAgICAgICAgc2l6ZSA9IDIuNSwKICAjICAgICAgICAgIHZqdXN0ID0gMS41LAogICMgICAgICAgICAgYWVzKGxhYmVsID0gZm9ybWF0KC4uY291bnQuLiwgYmlnLm1hcmsgPSAiLCIpCiAgIyAgICAgICAgICAgICApLAogICMgICAgICAgICApICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMCwgMTgwKSwgeWxpbSA9IGMoMCwgMC4wMikKICAgICAgICAgICAgICAgICApICsKICAjICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArCiAgbGFicyh0aXRsZSA9ICJWYXJpYXRpb24gaW4gVHJhdmVsIFRpbWUiLAogICAgICAgeCA9ICJUcmF2ZWwgVGltZSAoc2VjKSIsCiAgICAgICB5ID0gIkRlbnNpdHkiCiAgICAgICkKClRyYXZUaW1lX1NlY19IaXN0RGVuCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFRyYXZlbFRpbWVfU2VjLgoKVHJhdmVsVGltZV9TZWMgdmFsdWVzIGFyZSBOQS4KYGBge3J9CgpzdW1tYXJ5KEFsbERheXNfTmV3VHJhdmVsRGlzdCRUcmF2ZWxUaW1lX1NlYykKCgpWaWV3KHNlbGVjdChBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgIC1tYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICApICU+JSAKICAgICAgIGZpbHRlcihpcy5uYShUcmF2ZWxUaW1lX1NlYykgJgogICAgICAgICAgICAgICAgQnVzRGF5X0V2ZW50TnVtICE9IDEgICMgVHJhdmVsVGltZSBwdXJwb3NlZnVsbHkgbm90IGNhbGN1bGF0ZWQgaGVyZQogICAgICAgICAgICAgKQogICAgKQoKIyBleGFtcGxlcyBvZiBUcmF2ZWxUaW1lX1NlYyB2YWx1ZXMgdGhhdCBhcmUgTkEuIFRoZXNlIGFyZSBOQSBiZWNhdXNlIHRoZSBFdmVudF9UaW1lICYgRGVwYXJ0dXJlX1RpbWUgcmVhZGluZ3MgYXJlIG5vdCBhY2N1cmF0ZSAoaS5lLiwgdGhlIHByZXZpb3VzIERlcGFydHVyZV9UaW1lIGlzIEJFRk9SRSBvciBFUVVBTCBUTyB0aGUgY3VycmVudCBFdmVudF9UaW1lKS4KVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAoUm93TnVtX09HID49IDkwODA5ICYgUm93TnVtX09HIDw9IDkwODI5KSB8ICMgOTA4MTkKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gOTA4ODEgJiBSb3dOdW1fT0cgPD0gOTA5MDEpIHwgIyA5MDg5MQogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAyNTk3MDY2ICYgUm93TnVtX09HIDw9IDI1OTcwODYpIHwgIyAyNTk3MDc2CiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDI2MTMzMDUgJiBSb3dOdW1fT0cgPD0gMjYxMzMyNSkgIyAyNjEzMzE1CiAgICAgICAgICAgKSAlPiUgCiAgICAgICBzZWxlY3QoLW1hdGNoZXMoIihxKDJ8NXwoOTUpfCg5OCkpKXxNZWFufE1lZHxDbnQiKSkKICAgICkKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgVHJhdmVsVGltZV9TZWMuCgpUcmF2ZWxUaW1lX1NlYyB2YWx1ZXMgYXJlIGV4dHJlbWVseSBzbWFsbC4KYGBge3J9CgpWaWV3KHNlbGVjdChBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgIC1tYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICApICU+JSAKICAgICAgIGZpbHRlcighaXMubmEoVHJhdmVsVGltZV9TZWMpCiAgICAgICAgICAgICApICU+JSAKICAgICAgIGFycmFuZ2UoVHJhdmVsVGltZV9TZWMsCiAgICAgICAgICAgICAgIGRlc2MoU3BlZWRBdmdfTXBoX05ld0h2cnMpCiAgICAgICAgICAgICAgKSAlPiUKICAgICAgIGhlYWQoNTAwKQogICAgKQoKIyBleGFtcGxlcyB3aGVyZSBUcmF2ZWxUaW1lX1NlYyBpcyBzbWFsbCAoMSBzZWMpIGFuZCBTcGVlZEF2Z19NcGhfTmV3SHZycyBpcyBsYXJnZS4KVmlldyhzZWxlY3QoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAtbWF0Y2hlcygiKHEoMnw1fCg5NSl8KDk4KSkpfE1lYW58TWVkfENudCIpCiAgICAgICAgICAgKSAlPiUgCiAgICAgICBmaWx0ZXIoKFJvd051bV9PRyA+PSAyMjE3MzUzICYgUm93TnVtX09HIDw9IDIyMTczNzMpIHwgIyAyMjE3MzYzCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDMwOTAzMjEgJiBSb3dOdW1fT0cgPD0gMzA5MDM0MSkgfCAjIDMwOTAzMzEKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gODA3NjQgJiBSb3dOdW1fT0cgPD0gODA3ODQpIHwgIyA4MDc3NAogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAzMzg0MCAmIFJvd051bV9PRyA8PSAzMzg2MCkgIyAzMzg1MAogICAgICAgICAgICkKICAgICkKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgVHJhdmVsVGltZV9TZWMuCgpUcmF2ZWxUaW1lX1NlYyB2YWx1ZXMgYXJlIGV4dHJlbWVseSBsYXJnZS4KYGBge3J9CgpWaWV3KHNlbGVjdChBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgIC1tYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICApICU+JSAKICAgICAgIGZpbHRlcighaXMubmEoVHJhdmVsVGltZV9TZWMpCiAgICAgICAgICAgICApICU+JSAKICAgICAgIGFycmFuZ2UoZGVzYyhUcmF2ZWxUaW1lX1NlYyksCiAgICAgICAgICAgICAgIFNwZWVkQXZnX01waF9OZXdIdnJzCiAgICAgICAgICAgICAgKSAlPiUKICAgICAgIGhlYWQoNTAwKQogICAgKQoKIyBleGFtcGxlcyB3aGVyZSBUcmF2ZWxUaW1lX1NlYyBpcyBsYXJnZSBhbmQgU3BlZWRBdmdfTXBoX05ld0h2cnMgaXMgc21hbGwuClZpZXcoc2VsZWN0KEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgLW1hdGNoZXMoIihxKDJ8NXwoOTUpfCg5OCkpKXxNZWFufE1lZHxDbnQiKQogICAgICAgICAgICkgJT4lIAogICAgICAgZmlsdGVyKChSb3dOdW1fT0cgPj0gMTAwNzcwMyAmIFJvd051bV9PRyA8PSAxMDA3NzIzKSB8ICMgMTAwNzcxMwogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAyMzczNTY0ICYgUm93TnVtX09HIDw9IDIzNzM1ODQpIHwgIyAyMzczNTc0CiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDg2NDM3OSAmIFJvd051bV9PRyA8PSA4NjQzOTkpIHwgIyA4NjQzODkKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gMjU3MDA2MCAmIFJvd051bV9PRyA8PSAyNTcwMDgwKSAjIDI1NzAwNzAKICAgICAgICAgICApCiAgICApCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFRyYXZlbFRpbWVfU2VjLgoKQXJlIGxhcmdlIFRyYXZlbFRpbWVfU2VjIHZhbHVlcyByZWxhdGVkIHRvIFJvdXRlQ2hhbmdlcz8gTG9va3MgbGlrZWx5LiBXaGVuIHRoZSBCdXMgaW52b2x2ZXMgYSBSb3V0ZSAiY2hhbmdlIiwgdGhlcmUgaXMgYWxtb3N0IHR3aWNlIGFzIGxpa2VseSB0byBiZSBhIGNhc2Ugb2YgYW4gb3V0bGllciBUcmF2ZWxUaW1lX1NlYyB2YWx1ZSAob24gdGhlIGhpZ2ggc2lkZSkuCmBgYHtyfQoKVFRMYXJnZVJ0ZUNobmcgPC0gc2VsZWN0KEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgICAgICAgICAgICAgIC1tYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKFRUX091dCA9IGZhY3RvcihpZmVsc2UoVHJhdmVsVGltZV9TZWMgPiA0NjQsICAjIHRoaXMgaXMgdGhlIDk5dGggcGVyY2VudGlsZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJPdXRsaWVyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTm9ybWFsIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgKQoKIyBzdHIoVFRMYXJnZVJ0ZUNobmcpCgoKVFRMYXJnZVJ0ZUNobmdfQ250cyA8LSBncm91cF9ieShUVExhcmdlUnRlQ2huZywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSdGVDaGFuZ2UyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX091dAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgc3VtbWFyaXNlKENudHMgPSBuKCkKICAgICAgICAgICApCgpUVExhcmdlUnRlQ2huZ19TcHJlYWQgPC0gYXMuZGF0YS5mcmFtZShzcHJlYWQoVFRMYXJnZVJ0ZUNobmdfQ250cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX091dCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENudHMKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lCiAgc2VsZWN0KC1SdGVDaGFuZ2UyKQoKcm93Lm5hbWVzKFRUTGFyZ2VSdGVDaG5nX1NwcmVhZCkgPC0gYygiQ2hhbmdlIiwgIlNhbWUiKQojIHN0cihUVExhcmdlUnRlQ2huZ19TcHJlYWQpCgoKIyBXaGVuIHRoZSBCdXMgaW52b2x2ZXMgYSBSb3V0ZSAiY2hhbmdlIiwgdGhlcmUgaXMgYWxtb3N0IHR3aWNlIGFzIGxpa2VseSB0byBiZSBhIGNhc2Ugb2YgYW4gb3V0bGllciBUcmF2ZWxUaW1lX1NlYyB2YWx1ZS4KVFRMYXJnZVJ0ZUNobmdfU3ByZWFkCnByb3AudGFibGUoYXMudGFibGUoYXMubWF0cml4KFRUTGFyZ2VSdGVDaG5nX1NwcmVhZCkKICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgMQogICAgICAgICAgKQoKcHJvcC50YWJsZShhcy50YWJsZShhcy5tYXRyaXgoVFRMYXJnZVJ0ZUNobmdfU3ByZWFkKQogICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAyCiAgICAgICAgICApCgojIHJtKFRUTGFyZ2VSdGVDaG5nLCBUVExhcmdlUnRlQ2huZ19TcHJlYWQpCiAgICAgICAgIApgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFRyYXZlbFRpbWVfU2VjLgoKQXJlIGxhcmdlIFRyYXZlbFRpbWVfU2VjIHZhbHVlcyByZWxhdGVkIHRvIFJvdXRlQ2hhbmdlcz8gTG9va3MgbGlrZWx5LgpgYGB7cn0KClZpZXcoZmlsdGVyKFRUTGFyZ2VSdGVDaG5nLAogICAgICAgICAgICAhaXMubmEoVHJhdmVsVGltZV9TZWMpICYKICAgICAgICAgICAgICBSdGVDaGFuZ2UyID09ICJTYW1lIgogICAgICAgICAgICkgJT4lIAogICAgICAgYXJyYW5nZShkZXNjKFRyYXZlbFRpbWVfU2VjKSwKICAgICAgICAgICAgICAgU3BlZWRBdmdfTXBoX05ld0h2cnMKICAgICAgICAgICAgICApICU+JQogICAgICAgaGVhZCg1MDApCiAgICApCgoKIyBleGFtcGxlcyB3aGVyZSBUcmF2ZWxUaW1lX1NlYyBpcyBsYXJnZSBhbmQgU3BlZWRBdmdfTXBoX05ld0h2cnMgaXMgc21hbGwuClZpZXcoZmlsdGVyKFRUTGFyZ2VSdGVDaG5nLAogICAgICAgICAgICAoUm93TnVtX09HID49IDIyNTAyOTAgJiBSb3dOdW1fT0cgPD0gMjI1MDMxMCkgfCAjIDIyNTAzMDAKICAgICAgICAgICAgICAoUm93TnVtX09HID49IDg2NzcxNyAmIFJvd051bV9PRyA8PSA4Njc3MzcpIHwgIyA4Njc3MjcKICAgICAgICAgICAgICAoUm93TnVtX09HID49IDg2NDM3OSAmIFJvd051bV9PRyA8PSA4NjQzOTkpIHwgIyA4NjQzODkKICAgICAgICAgICAgICAoUm93TnVtX09HID49IDgwODM5NSAmIFJvd051bV9PRyA8PSA4MDg0MTUpICMgODA4NDA1CiAgICAgICAgICAgKQogICAgKQoKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgVHJhdmVsVGltZV9TZWMuCgpJZiBUcmF2ZWxUaW1lX1NlYyBpcyBiZWxvdyB0aGUgNXRoIHBlcmNlbnRpbGUgZm9yIHRoYXQgU3RhcnRTdG9wX0lELCBvciBpZiBUcmF2ZWxUaW1lX1NlYyBpcyBhYm92ZSB0aGUgOTV0aCBwZXJjZW50aWxlIGZvciB0aGF0IFN0YXJ0U3RvcF9JRCwgIGNvbnNpZGVyIHRoaXMgYW4gb3V0bGllci4gIEluIHRoaXMgY2FzZSwgcmVwbGFjZSB0aGUgdmFsdWUgd2l0aCB0aGUgbWVhbiBmb3IgdGhhdCBTdGFydFN0b3BfSUQgYW5kIEhvdXJHcm91cCAoVFRfU2VjX1NTSEdfTWVhbl9GKSwgb3IgaWYgdGhlcmUgYXJlIG5vdCBlbm91Z2ggdmFsdWVzIGF0IHRoZSBIb3VyR3JvdXAgbGV2ZWwsIHJlcGxhY2UgaXQgd2l0aCB0aGUgbWVhbiBmb3IgdGhhdCBTdGFydFN0b3BfSUQuCmBgYHtyfQoKcm0oVFRMYXJnZVJ0ZUNobmcsIFRUTGFyZ2VSdGVDaG5nX0NudHMsIFRUTGFyZ2VSdGVDaG5nX1NwcmVhZCkKCgpOZXdUcmF2VGltZSA8LSBtdXRhdGUoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX05ldyA9IGlmZWxzZSghaXMubmEoVHJhdmVsVGltZV9TZWMpICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoVHJhdmVsVGltZV9TZWMgPCBUVF9TZWNfU1NIR19xNSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsVGltZV9TZWMgPiBUVF9TZWNfU1NIR19xOTUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NIR19DbnRfRiA+PSAyMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX1NTSEdfTWVhbl9GLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZSghaXMubmEoVHJhdmVsVGltZV9TZWMpICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoVHJhdmVsVGltZV9TZWMgPCBUVF9TZWNfU1NIR19xNSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsVGltZV9TZWMgPiBUVF9TZWNfU1NIR19xOTUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NIR19DbnRfRiA8IDIwICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NfQ250X0YgPj0gMjAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU19NZWFuX0YsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKCFpcy5uYShUcmF2ZWxUaW1lX1NlYykgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChUcmF2ZWxUaW1lX1NlYyA8IFRUX1NlY19TU0hHX3E1IHwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxUaW1lX1NlYyA+IFRUX1NlY19TU0hHX3E5NQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU19DbnRfRiA8IDIwICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NfQ250ID49IDIwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NfTWVhbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoIWlzLm5hKFRyYXZlbFRpbWVfU2VjKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKFRyYXZlbFRpbWVfU2VjIDwgVFRfU2VjX1NTSEdfcTUgfAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjID4gVFRfU2VjX1NTSEdfcTk1CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX1NTX0NudF9GIDwgMjAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU19DbnQgPCAyMCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUnRlQ2hhbmdlMiA9PSAiQ2hhbmdlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTkEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSkpKSwKICAgICAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX05ld19MYWJlbCA9IAogICAgICAgICAgIGZhY3RvcihpZmVsc2UoIWlzLm5hKFRyYXZlbFRpbWVfU2VjKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgIChUcmF2ZWxUaW1lX1NlYyA8IFRUX1NlY19TU0hHX3E1IHwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsVGltZV9TZWMgPiBUVF9TZWNfU1NIR19xOTUKICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU0hHX0NudF9GID49IDIwLAogICAgICAgICAgICAgICAgICAgICAgICAgIlRUX1NlY19TU0hHX01lYW5fRiIsCiAgICAgICAgICAgICAgICAgIGlmZWxzZSghaXMubmEoVHJhdmVsVGltZV9TZWMpICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgKFRyYXZlbFRpbWVfU2VjIDwgVFRfU2VjX1NTSEdfcTUgfAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxUaW1lX1NlYyA+IFRUX1NlY19TU0hHX3E5NQogICAgICAgICAgICAgICAgICAgICAgICAgICApICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX1NTSEdfQ250X0YgPCAyMCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU19DbnRfRiA+PSAyMCwKICAgICAgICAgICAgICAgICAgICAgICAgICJUVF9TZWNfU1NfTWVhbl9GIiwKICAgICAgICAgICAgICAgICAgaWZlbHNlKCFpcy5uYShUcmF2ZWxUaW1lX1NlYykgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAoVHJhdmVsVGltZV9TZWMgPCBUVF9TZWNfU1NIR19xNSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjID4gVFRfU2VjX1NTSEdfcTk1CiAgICAgICAgICAgICAgICAgICAgICAgICAgICApICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX1NTX0NudF9GIDwgMjAgJgogICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NfQ250ID49IDIwLAogICAgICAgICAgICAgICAgICAgICAgICAgIlRUX1NlY19TU19NZWFuIiwKICAgICAgICAgICAgICAgICAgaWZlbHNlKCFpcy5uYShUcmF2ZWxUaW1lX1NlYykgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAoVHJhdmVsVGltZV9TZWMgPCBUVF9TZWNfU1NIR19xNSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjID4gVFRfU2VjX1NTSEdfcTk1CiAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJgogICAgICAgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfU1NfQ250X0YgPCAyMCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFRUX1NlY19TU19DbnQgPCAyMCAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgIFJ0ZUNoYW5nZTIgPT0gIkNoYW5nZSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBOQSwKICAgICAgICAgICAgICAgICAgICAgICAgICJUcmF2ZWxUaW1lX1NlYyIKICAgICAgICAgICAgICAgICAgICAgICAgKSkpKQogICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgIAogICAgICAgICAgICAgICAgICBUVF9Icl9OZXcgPSBUVF9TZWNfTmV3IC8gKDYwICogNjApCiAgICAgICAgICAgKQoKCmRpbShBbGxEYXlzX05ld1RyYXZlbERpc3QpCmRpbShOZXdUcmF2VGltZSkKcm0oQWxsRGF5c19OZXdUcmF2ZWxEaXN0KQoKc3VtbWFyeShzZWxlY3QoTmV3VHJhdlRpbWUsCiAgICAgICAgICAgLW1hdGNoZXMoIihxKDJ8NXwoOTUpfCg5OCkpKXxNZWFufE1lZHxDbnQiKQogICAgICAgICAgKQogICApCgpzdHIoc2VsZWN0KE5ld1RyYXZUaW1lLAogICAgICAgICAgIFRyYXZlbFRpbWVfU2VjLAogICAgICAgICAgIFRUX1NlY19OZXcsCiAgICAgICAgICAgVFRfU2VjX05ld19MYWJlbCwKICAgICAgICAgICBUVF9Icl9OZXcKICAgICAgICAgICkKICAgKQoKCnN1bW1hcnkoc2VsZWN0KE5ld1RyYXZUaW1lLAogICAgICAgICAgICAgICBUcmF2ZWxUaW1lX1NlYywKICAgICAgICAgICAgICAgVFRfU2VjX05ldywKICAgICAgICAgICAgICAgVFRfU2VjX05ld19MYWJlbCwKICAgICAgICAgICAgICAgVFRfSHJfTmV3CiAgICAgICAgICAgICAgKQogICAgICAgKQoKYGBgCgoKVGVzdCBpbnZlc3RpZ2F0aW9uIG9mIGp1c3QgdGhlIFgyIFJvdXRlLiBCb3ggcGxvdHMgZm9yIHRpbWUgYmV0d2VlbiBidXMgYXJyaXZhbHMgKGJ5IEhvdXJHcm91cCkuCmBgYHtyfQoKVmlldyhoZWFkKHNlbGVjdChOZXdUcmF2VGltZSwKICAgICAgICAgICAgICAgICAtbWF0Y2hlcygiKHEoMnw1fCg5NSl8KDk4KSkpfE1lYW58TWVkfENudCIpCiAgICAgICAgICAgICAgICApCiAgICAgICAgICkKICAgICkKClgyIDwtIHNlbGVjdChOZXdUcmF2VGltZSwKICAgICAgICAgICAgIC1tYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICAgKSAlPiUgCiAgZmlsdGVyKFJvdXRlID09ICJYMiIpCgpzdHIoWDIpCgpWaWV3KGhlYWQoYXJyYW5nZShYMiwKICAgICAgICAgICAgICAgICAgQnVzX0lELAogICAgICAgICAgICAgICAgICBFdmVudF9UaW1lCiAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgIDUwMAogICAgICAgICApCiAgICApCgpYMl9CeVN0b3AgPC0gZ3JvdXBfYnkoWDIsCiAgICAgICAgICAgICAgICAgICAgICBTdG9wSURfQ2xlYW4KICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgYXJyYW5nZShTdG9wSURfQ2xlYW4sCiAgICAgICAgICBFdmVudF9UaW1lKSAlPiUgCiAgbXV0YXRlKEV2ZW50X1RpbWVfTDEgPSBsYWcoRXZlbnRfVGltZSksCiAgICAgICAgIFRpbWVUb0V2ZW50X1NlYyA9IGFzLm51bWVyaWMoRXZlbnRfVGltZSAtIEV2ZW50X1RpbWVfTDEpLAogICAgICAgICBUaW1lVG9FdmVudF9NaW4gPSBUaW1lVG9FdmVudF9TZWMgLyA2MAogICAgICAgICkKClZpZXcoaGVhZChYMl9CeVN0b3AsIDUwMCkpCgoKIyBDb3VudF9WYWx1ZXMgaXMgbmVlZGVkIHRvIGRpc3BsYXkgdGhlIG1lZGlhbnMgb24gdGhlIGJveCBwbG90cwpDb3VudF9WYWx1ZXMgPC0gZGRwbHkoYXMuZGF0YS5mcmFtZShYMl9CeVN0b3ApLAogICAgICAgICAgICAgICAgICAgICAgLihFdmVudF9UaW1lX0hyR3JvdXApLAogICAgICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlLAogICAgICAgICAgICAgICAgICAgICAgVmFsdWVfQ291bnRzID0gbWVkaWFuKFRpbWVUb0V2ZW50X01pbiwgbmEucm0gPSBUUlVFKQogICAgICAgICAgICAgICAgICAgICApCgpUaW1lQnR3RXZlbnRzX1gyX0JveFBsb3QgPC0gZ2dwbG90KHNlbGVjdChhcy5kYXRhLmZyYW1lKFgyX0J5U3RvcCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRpbWVUb0V2ZW50X01pbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRXZlbnRfVGltZV9Ickdyb3VwCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoZmFjdG9yKEV2ZW50X1RpbWVfSHJHcm91cCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRpbWVUb0V2ZW50X01pbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGZhY3RvcihFdmVudF9UaW1lX0hyR3JvdXApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSArIAogIGdlb21fYm94cGxvdChvdXRsaWVyLmNvbG91cj0icmVkIiwgbm90Y2g9VFJVRSwgbmEucm0gPSBUUlVFKSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBDb3VudF9WYWx1ZXMsCiAgICAgICAgICAgIGFlcyh5ID0gVmFsdWVfQ291bnRzLAogICAgICAgICAgICAgICAgbGFiZWwgPSBmb3JtYXQocm91bmQoVmFsdWVfQ291bnRzLCBkaWdpdHMgPSAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5zbWFsbCA9IDEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICApLAogICAgICAgICAgICBzaXplID0gMywKICAgICAgICAgICAgdmp1c3QgPSAtMC41CiAgICAgICAgICAgKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUpKSArCiAgY29vcmRfY2FydGVzaWFuKCMgeGxpbSA9IGMoMCwgMTgwKSwKICAgICAgICAgICAgICAgICAgeWxpbSA9IGMoMCwgMTIwKQogICAgICAgICAgICAgICAgICkgKwogIGxhYnModGl0bGUgPSAiSG93IE9mdGVuIGFuIFgyIEFycml2ZXMgYXQgYSBHaXZlbiBTdG9wIiwKICAgICAgIHggPSAiSG91ciBHcm91cCIsCiAgICAgICB5ID0gIlRpbWUgQmV0d2VlbiBCdXNzZXMgKG1pbikiCiAgICAgICkKClRpbWVCdHdFdmVudHNfWDJfQm94UGxvdAoKYGBgCgoKVGVzdCBpbnZlc3RpZ2F0aW9uIG9mIGp1c3QgdGhlIFgyIFJvdXRlLiBWaW9saW4gcGxvdHMgZm9yIHRpbWUgYmV0d2VlbiBidXMgYXJyaXZhbHMgKGJ5IEhvdXIgR3JvdXApLgpgYGB7cn0KClRpbWVCdHdFdmVudHNfWDJfVmlvbGluUGxvdCA8LSBnZ3Bsb3Qoc2VsZWN0KGFzLmRhdGEuZnJhbWUoWDJfQnlTdG9wKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGltZVRvRXZlbnRfTWluLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0hyR3JvdXAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoZmFjdG9yKEV2ZW50X1RpbWVfSHJHcm91cCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRpbWVUb0V2ZW50X01pbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGZhY3RvcihFdmVudF9UaW1lX0hyR3JvdXApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSArIAogIGdlb21fdmlvbGluKGRyYXdfcXVhbnRpbGVzID0gYygwLjI1LCAwLjUsIDAuNzUpLAogICAgICAgICAgICAgIHRyaW0gPSBUUlVFLAogICAgICAgICAgICAgIHNjYWxlID0gImNvdW50IiwKICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUsCiAgICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBOQSwKICAgICAgICAgICAgICBpbmhlcml0LmFlcyA9IFRSVUUKICAgICAgICAgICAgICkgKwogIGdlb21fdGV4dChkYXRhID0gQ291bnRfVmFsdWVzLAogICAgICAgICAgICBhZXMoeSA9IFZhbHVlX0NvdW50cywKICAgICAgICAgICAgICAgIGxhYmVsID0gZm9ybWF0KHJvdW5kKFZhbHVlX0NvdW50cywgZGlnaXRzID0gMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuc21hbGwgPSAxCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgc2l6ZSA9IDIuNSwKICAgICAgICAgICAgdmp1c3QgPSAtMC41CiAgICAgICAgICAgKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUpKSArCiAgY29vcmRfY2FydGVzaWFuKCMgeGxpbSA9IGMoMCwgMTgwKSwKICAgICAgICAgICAgICAgICAgeWxpbSA9IGMoMCwgODApCiAgICAgICAgICAgICAgICAgKSArCiAgbGFicyh0aXRsZSA9ICJIb3cgT2Z0ZW4gYW4gWDIgQXJyaXZlcyBhdCBhIEdpdmVuIFN0b3AiLAogICAgICAgeCA9ICJIb3VyIEdyb3VwIiwKICAgICAgIHkgPSAiVGltZSBCZXR3ZWVuIEJ1c3NlcyAobWluKSIKICAgICAgKQoKVGltZUJ0d0V2ZW50c19YMl9WaW9saW5QbG90CgpgYGAKCgpUZXN0IGludmVzdGlnYXRpb24gb2YganVzdCB0aGUgWDIgUm91dGUuIEJveCBwbG90cyBmb3IgdGltZSBiZXR3ZWVuIGJ1cyBhcnJpdmFscyAoYnkgWmlwIENvZGUpLgpgYGB7cn0KCiMgQ291bnRfVmFsdWVzIGlzIG5lZWRlZCB0byBkaXNwbGF5IHRoZSBtZWRpYW5zIG9uIHRoZSBib3ggcGxvdHMKQ291bnRfVmFsdWVzX3ogPC0gZGRwbHkoYXMuZGF0YS5mcmFtZShYMl9CeVN0b3ApLAogICAgICAgICAgICAgICAgICAgICAgICAuKFN0b3BfWmlwKSwKICAgICAgICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlLAogICAgICAgICAgICAgICAgICAgICAgICBWYWx1ZV9Db3VudHMgPSBtZWRpYW4oVGltZVRvRXZlbnRfTWluLCBuYS5ybSA9IFRSVUUpCiAgICAgICAgICAgICAgICAgICAgICAgKQoKVGltZUJ0d0V2ZW50c19YMl9Cb3hQbG90X3ogPC0gZ2dwbG90KHNlbGVjdChhcy5kYXRhLmZyYW1lKFgyX0J5U3RvcCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGltZVRvRXZlbnRfTWluLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0b3BfWmlwCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGZhY3RvcihTdG9wX1ppcCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGltZVRvRXZlbnRfTWluLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBmYWN0b3IoU3RvcF9aaXApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgKyAKICBnZW9tX2JveHBsb3Qob3V0bGllci5jb2xvdXI9InJlZCIsIG5vdGNoPVRSVUUsIG5hLnJtID0gVFJVRSkgKwogIGdlb21fdGV4dChkYXRhID0gQ291bnRfVmFsdWVzX3osCiAgICAgICAgICAgIGFlcyh5ID0gVmFsdWVfQ291bnRzLAogICAgICAgICAgICAgICAgbGFiZWwgPSBmb3JtYXQocm91bmQoVmFsdWVfQ291bnRzLCBkaWdpdHMgPSAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5zbWFsbCA9IDEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICApLAogICAgICAgICAgICBzaXplID0gMywKICAgICAgICAgICAgdmp1c3QgPSAtMC41CiAgICAgICAgICAgKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUpKSArCiAgY29vcmRfY2FydGVzaWFuKCMgeGxpbSA9IGMoMCwgMTgwKSwKICAgICAgICAgICAgICAgICAgeWxpbSA9IGMoMCwgMTAwKQogICAgICAgICAgICAgICAgICkgKwogIGxhYnModGl0bGUgPSAiSG93IE9mdGVuIGFuIFgyIEFycml2ZXMgYXQgYSBHaXZlbiBTdG9wIiwKICAgICAgIHggPSAiWmlwIENvZGUgb2YgRGVzdGluYXRpb24iLAogICAgICAgeSA9ICJUaW1lIEJldHdlZW4gQnVzc2VzIChtaW4pIgogICAgICApCgpUaW1lQnR3RXZlbnRzX1gyX0JveFBsb3RfegoKYGBgCgoKVGVzdCBpbnZlc3RpZ2F0aW9uIG9mIGp1c3QgdGhlIFgyIFJvdXRlLiBWaW9saW4gcGxvdHMgZm9yIHRpbWUgYmV0d2VlbiBidXMgYXJyaXZhbHMgKGJ5IFppcCBDb2RlKS4KYGBge3J9CgpUaW1lQnR3RXZlbnRzX1gyX1Zpb2xpblBsb3RfeiA8LSBnZ3Bsb3Qoc2VsZWN0KGFzLmRhdGEuZnJhbWUoWDJfQnlTdG9wKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUaW1lVG9FdmVudF9NaW4sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU3RvcF9aaXAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGZhY3RvcihTdG9wX1ppcCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGltZVRvRXZlbnRfTWluLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBmYWN0b3IoU3RvcF9aaXApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgKyAKICBnZW9tX3Zpb2xpbihkcmF3X3F1YW50aWxlcyA9IGMoMC4yNSwgMC41LCAwLjc1KSwKICAgICAgICAgICAgICB0cmltID0gVFJVRSwKICAgICAgICAgICAgICBzY2FsZSA9ICJjb3VudCIsCiAgICAgICAgICAgICAgbmEucm0gPSBUUlVFLAogICAgICAgICAgICAgIHNob3cubGVnZW5kID0gTkEsCiAgICAgICAgICAgICAgaW5oZXJpdC5hZXMgPSBUUlVFCiAgICAgICAgICAgICApICsKICBnZW9tX3RleHQoZGF0YSA9IENvdW50X1ZhbHVlc196LAogICAgICAgICAgICBhZXMoeSA9IFZhbHVlX0NvdW50cywKICAgICAgICAgICAgICAgIGxhYmVsID0gZm9ybWF0KHJvdW5kKFZhbHVlX0NvdW50cywgZGlnaXRzID0gMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuc21hbGwgPSAxCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgc2l6ZSA9IDIuNSwKICAgICAgICAgICAgdmp1c3QgPSAtMC41CiAgICAgICAgICAgKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUpKSArCiAgY29vcmRfY2FydGVzaWFuKCMgeGxpbSA9IGMoMCwgMTgwKSwKICAgICAgICAgICAgICAgICAgeWxpbSA9IGMoMCwgNjApCiAgICAgICAgICAgICAgICAgKSArCiAgbGFicyh0aXRsZSA9ICJIb3cgT2Z0ZW4gYW4gWDIgQXJyaXZlcyBhdCBhIEdpdmVuIFN0b3AiLAogICAgICAgeCA9ICJaaXAgQ29kZSBvZiBEZXN0aW5hdGlvbiIsCiAgICAgICB5ID0gIlRpbWUgQmV0d2VlbiBCdXNzZXMgKG1pbikiCiAgICAgICkKClRpbWVCdHdFdmVudHNfWDJfVmlvbGluUGxvdF96CgpgYGAKCgpXYWl0aW5nIHRpbWUgYW5hbHlzZXMuCgpNdW5naW5nIGFuZCBzYW1wbGluZyBkYXRhIHRvIGdvIGZyb20gdGltZSBiZXRlZW4gYnVzZXMgdG8gImF2ZXJhZ2UiIHdhaXRpbmcgdGltZS4KCkZpcnN0LCBnZXQgdGhlIG1heCBhbmQgbWluIHRpbWVzIG9mIGJ1cyBzdG9wcyAoZWFjaCBkYXksIGFuZCBmb3IgZWFjaCByb3V0ZSkuCmBgYHtyfQoKcm0oWDIsIFgyX0J5U3RvcCwgWDJfTG9uZywgWDJfUGN0KQoKClJvdXRlTWluTWF4IDwtIGdyb3VwX2J5KE5ld1RyYXZUaW1lLAogICAgICAgICAgICAgICAgICAgICAgICBSb3V0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgRXZlbnRfVGltZV9EYXRlCiAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgc3VtbWFyaXNlKE1pblRpbWUgPSBtaW4oRXZlbnRfVGltZSksCiAgICAgICAgICAgIE1heFRpbWUgPSBtYXgoRXZlbnRfVGltZSkKICAgICAgICAgICApCgpzdHIoUm91dGVNaW5NYXgpClZpZXcoUm91dGVNaW5NYXgpCgpgYGAKCgpXYWl0aW5nIHRpbWUgYW5hbHlzZXMuCgpNdW5naW5nIGFuZCBzYW1wbGluZyBkYXRhIHRvIGdvIGZyb20gdGltZSBiZXRlZW4gYnVzZXMgdG8gImF2ZXJhZ2UiIHdhaXRpbmcgdGltZS4KCihQdWxscyBoZXJlIGFyZSBkb25lIGJ5IGRheSwgYXMgdGhlIGRhdGEgYXJlIHRvbyBsYXJnZSB0byBkbyBhdCBvbmNlLikKYGBge3J9CgojIFZpZXcoaGVhZChOZXdUcmF2VGltZSwgNTAwKSkKCiMgRm9yIGVhY2ggcmVjb3JkLCBjcmVhdGUgYSByYW5kb20gZGF0ZXRpbWUgYmV0d2VlbiB0aGUgZmlyc3QgYW5kIGxhc3Qgc3RvcCBmb3IgdGhhdCBidXMgcm91dGUgKG9uIHRoYXQgZGF5KS4KZm9yKGkgaW4gMzo3KXsKCnNldC5zZWVkKDEyMzQ1Njc4OSkKU2FtcCA8LSBzZWxlY3QoTmV3VHJhdlRpbWUsCiAgICAgICAgICAgICAgIFJvd051bV9PRywKICAgICAgICAgICAgICAgUm91dGUsCiAgICAgICAgICAgICAgICMgUm91dGVHcm91cCwKICAgICAgICAgICAgICAgRXZlbnRfVGltZV9EYXRlLAogICAgICAgICAgICAgICBTdG9wSURfQ2xlYW4sCiAgICAgICAgICAgICAgIHN0YXJ0c193aXRoKCJFdmVudCIpCiAgICAgICAgICAgICAgKSAlPiUgCiAgZmlsdGVyKEV2ZW50X1RpbWVfRGF0ZSA9PSBpKSAlPiUgICMgbmVlZGVkIHRvIGRvIHRoaXMgZWFjaCBkYXkgKDMtNykgYmVjYXVzZSB0aGUgY29tcGxldGUgZmlsZSB3YXMgdG9vIGxhcmdlIHRvIGRvIGF0IG9uY2UKICBsZWZ0X2pvaW4oUm91dGVNaW5NYXgsCiAgICAgICAgICAgIGJ5ID0gYygiUm91dGUiID0gIlJvdXRlIiwKICAgICAgICAgICAgICAgICAgICJFdmVudF9UaW1lX0RhdGUiID0gIkV2ZW50X1RpbWVfRGF0ZSIKICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICkgJT4lIAogIG11dGF0ZShTYW1wVGltZSA9IGFzX2RhdGV0aW1lKHJ1bmlmKG5yb3coLiksICMyMDAwMDAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluID0gTWluVGltZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXggPSBNYXhUaW1lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR6ID0gIkFtZXJpY2EvTmV3X1lvcmsiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgKSAlPiUgCiAgYXJyYW5nZShSb3V0ZSwKICAgICAgICAgIFN0b3BJRF9DbGVhbiwKICAgICAgICAgIEV2ZW50X1RpbWUKICAgICAgICAgKSAKCiMgc3RyKFNhbXApCiMgVmlldyhoZWFkKFNhbXAsIDUwMCkpCiMgCiMgVmlldygKIyBncm91cF9ieShTYW1wLAojICAgICAgICAgIFJvd051bV9PRwojICAgICAgICAgKSAlPiUKIyAgIHN1bW1hcmlzZShDbnRfTnVtID0gbigpLAojICAgICAgICAgICAgIENudF9QY3QgPSAxMDAgKiBDbnRfTnVtIC8gbnJvdyhTYW1wKQojICAgICAgICAgICAgKSAlPiUKIyAgIGFycmFuZ2UoZGVzYyhDbnRfTnVtKSkKIyApCgoKIyBGb3IgZWFjaCBSb3V0ZSBhbmQgU3RvcElEIGNvbWJpbmF0aW9uLCBnZXQgYWxsIHRoZSBFdmVudF9UaW1lIHZhbHVlcyB0aGF0IGFyZSBhZnRlciB0aGUgU2FtcFRpbWUgdmFsdWUuCiMgZXN0aW1hdGluZyBhcHByb3ggMmhycyBvZiBydW50aW1lIGZvciBhbGwgMi44bSByZWNvcmRzClRlc3RpbmdfQSA8LSBzcWxkZigiICAgU2VsZWN0ICAgICAgICAgICAgICAgdDEuKgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICx0Mi5FdmVudF9UaW1lICAgICAgICAgICAgIGFzIE5leHRCdXMKICAgICAgICAgICAgICAgICAgICAgICAgRnJvbSAgICAgICAgICAgICAgICAgU2FtcCAgICAgICAgICAgICAgICAgICAgICBhcyB0MQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIElubmVyIEpvaW4gICAgICBTYW1wICAgICAgICAgICAgICAgICAgICAgIGFzIHQyCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgT24gICAgICAgICAgICAgIHQxLlJvdXRlID0gdDIuUm91dGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBbmQgICAgICAgICAgICAgdDEuU3RvcElEX0NsZWFuID0gdDIuU3RvcElEX0NsZWFuCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQW5kICAgICAgICAgICAgIHQyLkV2ZW50X1RpbWUgPiB0MS5TYW1wVGltZQogICAgICAgICAgICAgICAgICAgICAgICBPcmRlciBCeSAgICAgICAgICAgICB0MS5Sb3V0ZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICx0MS5TdG9wSURfQ2xlYW4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsdDEuRXZlbnRfVGltZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICx0Mi5FdmVudF9UaW1lCiAgICAgICAgICAgICAgICAgICIKICAgICAgICAgICAgICAgICApICU+JSAKICBtdXRhdGUoTkIgPSBhc19kYXRldGltZShOZXh0QnVzLAogICAgICAgICAgICAgICAgICAgICAgICAgIHR6ID0gIkFtZXJpY2EvTmV3X1lvcmsiCiAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgKQoKIyBzdHIoVGVzdGluZ19BKQojIFZpZXcoaGVhZChUZXN0aW5nX0EsIDUwMCkpCiMgVmlldyhoZWFkKFNhbXAsIDUwMCkpCgoKIyBGaWx0ZXIgdGhlIGRhdGFmcmFtZSB0byBvbmx5IGluY2x1ZGUgdGhlIGJ1cyBhcnJpdmFsIGF0IFN0b3BJRCB0aGF0IGlzIHRoZSBuZXh0IHRvIGNvbWUgYWZ0ZXIgdGhlIFNhbXBUaW1lLgojIGVzdGltYXRpbmcgYXBwcm94IDIwbWluIG9mIHJ1bnRpbWUgZm9yIGFsbCAyLjhtIHJlY29yZHMKVGVzdGluZyA8LSBzZWxlY3QoVGVzdGluZ19BLAogICAgICAgICAgICAgICAgICAtTmV4dEJ1cwogICAgICAgICAgICAgICAgICkgJT4lIAogIGdyb3VwX2J5KFJvd051bV9PRykgJT4lIAogIGZpbHRlcihOQiA9PSBtaW4oTkIpCiAgICAgICAgKSAlPiUgCiAgYXJyYW5nZShSb3V0ZSwKICAgICAgICAgIFN0b3BJRF9DbGVhbiwKICAgICAgICAgIEV2ZW50X1RpbWUKICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKFdhaXRUaW1lX01pbiA9IGFzLm51bWVyaWMoTkIgLSBTYW1wVGltZSksCiAgICAgICAgIFdhaXRUaW1lX1NlYyA9IFdhaXRUaW1lX01pbiAqIDYwLAogICAgICAgICBXYWl0VGltZV9TZWMyID0gTkIgLSBTYW1wVGltZSwKICAgICAgICAgV2FpdFRpbWVfTWluMiA9IFdhaXRUaW1lX1NlYzIgLyA2MAogICAgICAgICkgJT4lIAogIGFzLmRhdGEuZnJhbWUoKQoKYXNzaWduKHBhc3RlMCgiVGVzdGluZ18iLCBpKSwKICAgICAgIFRlc3RpbmcKICAgICAgKQoKcm0oU2FtcCxUZXN0aW5nX0EsIFRlc3RpbmcpCnN0cihnZXQocGFzdGUwKCJUZXN0aW5nXyIsIGkpKSkKVmlldyhnZXQocGFzdGUwKCJUZXN0aW5nXyIsIGkpKSkKfQoKCiMgQmluZCBhbGwgdGhlIGluZGl2aWR1YWwgZGF0YWZyYW1lcyB0b2dldGhlci4KV2FpdERhdGFfRGF5UHVsbCA8LSBiaW5kX3Jvd3MoVGVzdGluZ18zLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUZXN0aW5nXzQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRlc3RpbmdfNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVGVzdGluZ182LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUZXN0aW5nXzcKICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBtdXRhdGUoV2FpdFRpbWVfU2VjMyA9IE5CIC0gU2FtcFRpbWUsCiAgICAgICAgIFdhaXRUaW1lX01pbjMgPSBXYWl0VGltZV9TZWMzIC8gNjAKICAgICAgICApICU+JSAKICBhcnJhbmdlKFJvdXRlLAogICAgICAgICAgU3RvcElEX0NsZWFuLAogICAgICAgICAgRXZlbnRfVGltZQogICAgICAgICApCgpybShUZXN0aW5nXzMsIFRlc3RpbmdfNCwgVGVzdGluZ181LCBUZXN0aW5nXzYsIFRlc3RpbmdfNykKc3RyKFdhaXREYXRhX0RheVB1bGwpClZpZXcoaGVhZChXYWl0RGF0YV9EYXlQdWxsLCA1MDApKQpWaWV3KHRhaWwoV2FpdERhdGFfRGF5UHVsbCwgNTAwKSkKCmBgYAoKCldhaXRpbmcgdGltZSBhbmFseXNlcy4KCk11bmdpbmcgYW5kIHNhbXBsaW5nIGRhdGEgdG8gZ28gZnJvbSB0aW1lIGJldGVlbiBidXNlcyB0byAiYXZlcmFnZSIgd2FpdGluZyB0aW1lLgoKQmFzaWMgaW52ZXN0aWdhdGlvbiBvZiBhbnkgbWlzc2luZyByb3dzIGZyb20gZGF0YSBwdWxsZWQgYnkgZGF5LgpgYGB7cn0KCkRpc3RpbmN0Um93TnVtX09HIDwtIGRpc3RpbmN0KHNlbGVjdChXYWl0RGF0YV9EYXlQdWxsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUm93TnVtX09HCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCgpzdHIoRGlzdGluY3RSb3dOdW1fT0cpCgojIFZpZXcoCiMgYW50aV9qb2luKFNhbXAsCiMgICAgICAgICAgIERpc3RpbmN0Um93TnVtX09HLAojICAgICAgICAgICBieSA9IGMoIlJvd051bV9PRyIgPSAiUm93TnVtX09HIikKIyAgICAgICAgICApCiMgKQoKCiMgVGhlIHNhbXAgdGltZSBpcyBBRlRFUiB0aGUgbGFzdCBidXMgcGFzc2VkIHRoYXQgU3RvcElEX0NsZWFuCiMgVmlldyhmaWx0ZXIoU2FtcCwKIyAgICAgICAgICAgICBFdmVudF9UaW1lID4gIjIwMTYtMTAtMDcgMTk6NDg6NDEiICYKIyAgICAgICAgICAgICAgIFJvdXRlID09ICJYMiIgJgojICAgICAgICAgICAgICAgU3RvcElEX0NsZWFuID09IDEwMDM3NzQKIyAgICAgICAgICAgICkKIyAgICAgKQoKIyBOZXh0IEJ1cyAoTkIpIGNhbiBiZSBvbiB0aGUgbmV4dCBtb3JuaW5nCiMgVmlldyhmaWx0ZXIoVGVzdGluZzcsCiMgICAgICAgICAgICAgU2FtcFRpbWUgPiAiMjAxNi0xMC0wNiAyMzo1ODowMCIgJgojICAgICAgICAgICAgICAgU2FtcFRpbWUgPCAiMjAxNi0xMC0wNiAyMzo1OTo1OSIpCiMgICAgICkKCmBgYAoKCldhaXRpbmcgdGltZSBhbmFseXNlcy4KCk11bmdpbmcgYW5kIHNhbXBsaW5nIGRhdGEgdG8gZ28gZnJvbSB0aW1lIGJldGVlbiBidXNlcyB0byAiYXZlcmFnZSIgd2FpdGluZyB0aW1lLgoKKFB1bGxzIGhlcmUgYXJlIGRvbmUgYnkgZ3JvdXBpbmdzIG9mIGJ1cyByb3V0ZXMsIGFzIHRoZSBkYXRhIGFyZSB0b28gbGFyZ2UgdG8gZG8gYXQgb25jZS4pCgpGaXJzdCwgd2UgbmVlZCB0byBmaW5kIHRoZSBtb3N0IGNvbW1vbiBidXMgcm91dGVzLgpgYGB7cn0KCnJtKERpc3RpbmN0Um93TnVtX09HKQoKCiMgVmlldyhoZWFkKE5ld1RyYXZUaW1lLCA1MDApKQoKc2V0LnNlZWQoMTIzNDU2Nzg5KQpCdXNHcm91cHMgPC0gZ3JvdXBfYnkoTmV3VHJhdlRpbWUsCiAgICAgICAgICAgICAgICAgICAgICBSb3V0ZQogICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzdW1tYXJpc2UoQ250X051bSA9IG4oKSwKICAgICAgICAgICAgQ250X1BjdCA9IENudF9OdW0gLyBucm93KE5ld1RyYXZUaW1lKQogICAgICAgICAgICkgJT4lIAogIGFycmFuZ2UoZGVzYyhDbnRfTnVtKQogICAgICAgICApICU+JSAKICBtdXRhdGUoUm93TnVtID0gcm93X251bWJlcigpLAogICAgICAgICBSYW5kTnVtID0gcnVuaWYobiA9IDI2OCksCiAgICAgICAgIFJvdXRlR3JvdXAgPSBpZmVsc2UoUmFuZE51bSA8PSAwLjIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMSwKICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShSYW5kTnVtIDw9IDAuNCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAyLAogICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFJhbmROdW0gPD0gMC42LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIDMsCiAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoUmFuZE51bSA8PSAwLjgsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgNCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICA1CiAgICAgICAgICAgICAgICAgICAgICAgICAgICApKSkpCiAgICAgICAgKQoKc3RyKEJ1c0dyb3VwcykKVmlldyhCdXNHcm91cHMpCnN1bW1hcnkoQnVzR3JvdXBzKQoKYGBgCgoKV2FpdGluZyB0aW1lIGFuYWx5c2VzLgoKTXVuZ2luZyBhbmQgc2FtcGxpbmcgZGF0YSB0byBnbyBmcm9tIHRpbWUgYmV0ZWVuIGJ1c2VzIHRvICJhdmVyYWdlIiB3YWl0aW5nIHRpbWUuCgooUHVsbHMgaGVyZSBhcmUgZG9uZSBieSBncm91cGluZ3Mgb2YgYnVzIHJvdXRlcywgYXMgdGhlIGRhdGEgYXJlIHRvbyBsYXJnZSB0byBkbyBhdCBvbmNlLikKYGBge3J9CgojIFZpZXcoaGVhZChOZXdUcmF2VGltZSwgNTAwKSkKCiMgRm9yIGVhY2ggcmVjb3JkLCBjcmVhdGUgYSByYW5kb20gZGF0ZXRpbWUgYmV0d2VlbiB0aGUgZmlyc3QgYW5kIGxhc3Qgc3RvcCBmb3IgdGhhdCBidXMgcm91dGUgKG9uIHRoYXQgZGF5KS4KZm9yKGkgaW4gMTo1KXsKICAKc2V0LnNlZWQoMTIzNDU2Nzg5KQpTYW1wIDwtIGxlZnRfam9pbihOZXdUcmF2VGltZSwKICAgICAgICAgICAgICAgICAgQnVzR3JvdXBzLAogICAgICAgICAgICAgICAgICBieSA9IGMoIlJvdXRlIiA9ICJSb3V0ZSIpCiAgICAgICAgICAgICAgICAgICkgJT4lIAogIHNlbGVjdChSb3dOdW1fT0csCiAgICAgICAgIFJvdXRlLAogICAgICAgICBSb3V0ZUdyb3VwLAogICAgICAgICBFdmVudF9UaW1lX0RhdGUsCiAgICAgICAgIFN0b3BJRF9DbGVhbiwKICAgICAgICAgc3RhcnRzX3dpdGgoIkV2ZW50IikKICAgICAgICApICU+JSAKICBmaWx0ZXIoUm91dGVHcm91cCA9PSBpKSAlPiUgICMgbmVlZGVkIHRvIGRvIHRoaXMgZWFjaCBSb3V0ZUdyb3VwICgxLTUpIGJlY2F1c2UgdGhlIGNvbXBsZXRlIGZpbGUgd2FzIHRvbyBsYXJnZSB0byBkbyBhdCBvbmNlCiAgbGVmdF9qb2luKFJvdXRlTWluTWF4LAogICAgICAgICAgICBieSA9IGMoIlJvdXRlIiA9ICJSb3V0ZSIsCiAgICAgICAgICAgICAgICAgICAiRXZlbnRfVGltZV9EYXRlIiA9ICJFdmVudF9UaW1lX0RhdGUiCiAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICApICU+JSAKICBtdXRhdGUoU2FtcFRpbWUgPSBhc19kYXRldGltZShydW5pZihucm93KC4pLCAjMjAwMDAwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbiA9IE1pblRpbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWF4ID0gTWF4VGltZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eiA9ICJBbWVyaWNhL05ld19Zb3JrIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICkgJT4lIAogIGFycmFuZ2UoUm91dGUsCiAgICAgICAgICBTdG9wSURfQ2xlYW4sCiAgICAgICAgICBFdmVudF9UaW1lCiAgICAgICAgICkgCgojIHN0cihTYW1wKQojIFZpZXcoaGVhZChTYW1wLCA1MDApKQojIAojIFZpZXcoCiMgZ3JvdXBfYnkoU2FtcCwKIyAgICAgICAgICBSb3dOdW1fT0cKIyAgICAgICAgICkgJT4lCiMgICBzdW1tYXJpc2UoQ250X051bSA9IG4oKSwKIyAgICAgICAgICAgICBDbnRfUGN0ID0gMTAwICogQ250X051bSAvIG5yb3coU2FtcCkKIyAgICAgICAgICAgICkgJT4lCiMgICBhcnJhbmdlKGRlc2MoQ250X051bSkpCiMgKQoKCiMgRm9yIGVhY2ggUm91dGUgYW5kIFN0b3BJRCBjb21iaW5hdGlvbiwgZ2V0IGFsbCB0aGUgRXZlbnRfVGltZSB2YWx1ZXMgdGhhdCBhcmUgYWZ0ZXIgdGhlIFNhbXBUaW1lIHZhbHVlLgojIGVzdGltYXRpbmcgYXBwcm94IDJocnMgb2YgcnVudGltZSBmb3IgYWxsIDIuOG0gcmVjb3JkcwpUZXN0aW5nX0EgPC0gc3FsZGYoIiAgIFNlbGVjdCAgICAgICAgICAgICAgIHQxLioKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsdDIuRXZlbnRfVGltZSAgICAgICAgICAgICBhcyBOZXh0QnVzCiAgICAgICAgICAgICAgICAgICAgICAgIEZyb20gICAgICAgICAgICAgICAgIFNhbXAgICAgICAgICAgICAgICAgICAgICAgYXMgdDEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBJbm5lciBKb2luICAgICAgU2FtcCAgICAgICAgICAgICAgICAgICAgICBhcyB0MgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE9uICAgICAgICAgICAgICB0MS5Sb3V0ZSA9IHQyLlJvdXRlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQW5kICAgICAgICAgICAgIHQxLlN0b3BJRF9DbGVhbiA9IHQyLlN0b3BJRF9DbGVhbgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEFuZCAgICAgICAgICAgICB0Mi5FdmVudF9UaW1lID4gdDEuU2FtcFRpbWUKICAgICAgICAgICAgICAgICAgICAgICAgT3JkZXIgQnkgICAgICAgICAgICAgdDEuUm91dGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsdDEuU3RvcElEX0NsZWFuCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgLHQxLkV2ZW50X1RpbWUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAsdDIuRXZlbnRfVGltZQogICAgICAgICAgICAgICAgICAiCiAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKE5CID0gYXNfZGF0ZXRpbWUoTmV4dEJ1cywKICAgICAgICAgICAgICAgICAgICAgICAgICB0eiA9ICJBbWVyaWNhL05ld19Zb3JrIgogICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICkKCiMgc3RyKFRlc3RpbmdfQSkKIyBWaWV3KGhlYWQoVGVzdGluZ19BLCA1MDApKQojIFZpZXcoaGVhZChTYW1wLCA1MDApKQoKCiMgRmlsdGVyIHRoZSBkYXRhZnJhbWUgdG8gb25seSBpbmNsdWRlIHRoZSBidXMgYXJyaXZhbCBhdCBTdG9wSUQgdGhhdCBpcyB0aGUgbmV4dCB0byBjb21lIGFmdGVyIHRoZSBTYW1wVGltZS4KIyBlc3RpbWF0aW5nIGFwcHJveCAyMG1pbiBvZiBydW50aW1lIGZvciBhbGwgMi44bSByZWNvcmRzClRlc3RpbmcgPC0gc2VsZWN0KFRlc3RpbmdfQSwKICAgICAgICAgICAgICAgICAgLU5leHRCdXMKICAgICAgICAgICAgICAgICApICU+JSAKICBncm91cF9ieShSb3dOdW1fT0cpICU+JSAKICBmaWx0ZXIoTkIgPT0gbWluKE5CKQogICAgICAgICkgJT4lIAogIGFycmFuZ2UoUm91dGUsCiAgICAgICAgICBTdG9wSURfQ2xlYW4sCiAgICAgICAgICBFdmVudF9UaW1lCiAgICAgICAgICkgJT4lIAogIG11dGF0ZShXYWl0VGltZV9NaW4gPSBhcy5udW1lcmljKE5CIC0gU2FtcFRpbWUpLAogICAgICAgICBXYWl0VGltZV9TZWMgPSBXYWl0VGltZV9NaW4gKiA2MAogICAgICAgICkgJT4lIAogIGFzLmRhdGEuZnJhbWUoKQoKYXNzaWduKHBhc3RlMCgiVGVzdGluZyIsIGkpLAogICAgICAgVGVzdGluZwogICAgICApCgpybShTYW1wLFRlc3RpbmdfQSwgVGVzdGluZykKc3RyKGdldChwYXN0ZTAoIlRlc3RpbmciLCBpKSkpClZpZXcoZ2V0KHBhc3RlMCgiVGVzdGluZyIsIGkpKSkKfQoKCiMgQmluZCBhbGwgdGhlIGluZGl2aWR1YWwgZGF0YWZyYW1lcyB0b2dldGhlci4KV2FpdERhdGFfUm91dGVQdWxsIDwtIGJpbmRfcm93cyhUZXN0aW5nMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUZXN0aW5nMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUZXN0aW5nMywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUZXN0aW5nNCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUZXN0aW5nNQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIG11dGF0ZShXYWl0VGltZV9TZWMyID0gTkIgLSBTYW1wVGltZSwKICAgICAgICAgV2FpdFRpbWVfTWluMiA9IFdhaXRUaW1lX1NlYzIgLyA2MAogICAgICAgICkgJT4lIAogIGFycmFuZ2UoUm91dGUsCiAgICAgICAgICBTdG9wSURfQ2xlYW4sCiAgICAgICAgICBFdmVudF9UaW1lCiAgICAgICAgICkKCnJtKEJ1c0dyb3VwcywgaSwgVGVzdGluZzMsIFRlc3Rpbmc0LCBUZXN0aW5nNSwgVGVzdGluZzYsIFRlc3Rpbmc3KQpzdHIoV2FpdERhdGFfUm91dGVQdWxsKQpWaWV3KGhlYWQoV2FpdERhdGFfUm91dGVQdWxsLCA1MDApKQpWaWV3KHRhaWwoV2FpdERhdGFfUm91dGVQdWxsLCA1MDApKQoKYGBgCgoKV2FpdGluZyB0aW1lIGFuYWx5c2VzLgoKTXVuZ2luZyBhbmQgc2FtcGxpbmcgZGF0YSB0byBnbyBmcm9tIHRpbWUgYmV0ZWVuIGJ1c2VzIHRvICJhdmVyYWdlIiB3YWl0aW5nIHRpbWUuCgpDb21wYXJlIFdhaXREYXRhIHB1bGxlZCBieSBkYXkgYW5kIHB1bGxlZCBieSByb3V0ZS4KYGBge3J9CgpkaW0oV2FpdERhdGFfUm91dGVQdWxsKQpkaW0oV2FpdERhdGFfRGF5UHVsbCkKbnJvdyhXYWl0RGF0YV9Sb3V0ZVB1bGwpIC0gbnJvdyhXYWl0RGF0YV9EYXlQdWxsKQoKV2FpdERhdGFfRGlmZiA8LSBhbnRpX2pvaW4oV2FpdERhdGFfUm91dGVQdWxsLAogICAgICAgICAgICAgICAgICAgICAgICAgICBXYWl0RGF0YV9EYXlQdWxsLAogICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IGMoIlJvd051bV9PRyIgPSAiUm93TnVtX09HIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgc2VsZWN0KC1XYWl0VGltZV9NaW4sCiAgICAgICAgIC1XYWl0VGltZV9TZWMKICAgICAgICApCgpzdHIoV2FpdERhdGFfRGlmZikKVmlldyhoZWFkKFdhaXREYXRhX0RpZmYsIDUwMCkpCgpWaWV3KGZpbHRlcihXYWl0RGF0YV9Sb3V0ZVB1bGwsCiAgICAgICAgICAgIFJvdXRlID09ICJaOCIgJgogICAgICAgICAgICAgIFN0b3BJRF9DbGVhbiA9PSAyMDA1NDY1CiAgICAgICAgICAgICMgUm93TnVtX09HID0gMjkwMjc2MAogICAgICAgICAgICAjIEV2ZW50X1RpbWUgPSAyMDE2LTEwLTA3IDE5OjUxOjQ3CiAgICAgICAgICAgKQogICAgKQoKVmlldyhncm91cF9ieShXYWl0RGF0YV9EaWZmLAogICAgICAgICAgICAgIFJvdXRlCiAgICAgICAgICAgICApICU+JSAKICAgICAgIHN1bW1hcmlzZShDbnRfTnVtID0gbigpLAogICAgICAgICAgICAgICAgIENudF9QY3QgPSBDbnRfTnVtIC8gbnJvdyhXYWl0RGF0YV9EaWZmKQogICAgICAgICAgICAgICAgKSAlPiUgCiAgICAgICBhcnJhbmdlKGRlc2MoQ250X051bSkKICAgICAgICAgICAgICApCiAgICApCgpWaWV3KGZpbHRlcihXYWl0RGF0YV9EaWZmLAogICAgICAgICAgICBSb3V0ZSA9PSAiUzEiCiAgICAgICAgICAgKQogICAgKQoKVmlldyhmaWx0ZXIoV2FpdERhdGFfUm91dGVQdWxsLAogICAgICAgICAgICBSb3V0ZSA9PSAiUzEiICYKICAgICAgICAgICAgICBTdG9wSURfQ2xlYW4gPT0gMTAwMzEzMgogICAgICAgICAgICAjIFJvd051bV9PRyA9IDExNTE3NzAKICAgICAgICAgICAgIyBFdmVudF9UaW1lID0gMjAxNi0xMC0wNyAwOTowNzoxMgogICAgICAgICAgICkKICAgICkKCiMgQ2FuJ3QgdGVsbCB3aHkgdGhlIHB1bGwgYnkgZGF5IGhhcyBsZXNzIHJlY29yZHMgdGhhbiB0aGUgcHVsbCBieSByb3V0ZQoKYGBgCgoKV2FpdGluZyB0aW1lIGFuYWx5c2VzLgoKTXVuZ2luZyBhbmQgc2FtcGxpbmcgZGF0YSB0byBnbyBmcm9tIHRpbWUgYmV0ZWVuIGJ1c2VzIHRvICJhdmVyYWdlIiB3YWl0aW5nIHRpbWUuCgpDb21wYXJlIFdhaXREYXRhIChwdWxsZWQgYnkgcm91dGUpIGFuZCBvcmlnaW5hbCBkYXRhIChOZXdUcmF2VGltZSkuCmBgYHtyfQoKZGltKE5ld1RyYXZUaW1lKSAgIyAyLDgwOSw1Mjkgcm93cwpkaW0oV2FpdERhdGFfUm91dGVQdWxsKSAgIyAyLDc4MCw4NDggcm93cwpucm93KE5ld1RyYXZUaW1lKSAtIG5yb3coV2FpdERhdGFfUm91dGVQdWxsKSAgIyBpcyAyOCw2ODEgcm93cwoKc3RyKHNlbGVjdChOZXdUcmF2VGltZSwKICAgICAgICAgICAtbWF0Y2hlcygiKHEoMnw1fCg5NSl8KDk4KSkpfE1lYW58TWVkfENudCIpCiAgICAgICAgICApCiAgICkKc3RyKFdhaXREYXRhX1JvdXRlUHVsbCkKCkNvbXBhcmVfTlRUX1dEIDwtIGxlZnRfam9pbihOZXdUcmF2VGltZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlbGVjdChXYWl0RGF0YV9Sb3V0ZVB1bGwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUm93TnVtX09HLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgUm91dGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUm91dGVHcm91cCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFN0b3BJRF9DbGVhbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIEV2ZW50X1RpbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTWluVGltZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBNYXhUaW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFNhbXBUaW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5CLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFdhaXRUaW1lX1NlYzIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgV2FpdFRpbWVfTWluMgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gYygiUm93TnVtX09HIiA9ICJSb3dOdW1fT0ciKQogICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzZWxlY3QoLW1hdGNoZXMoIihxKDJ8NXwoOTUpfCg5OCkpKXxNZWFufE1lZHxDbnQiKQogICAgICAgICkgJT4lIAogIGFycmFuZ2UoUm91dGUsCiAgICAgICAgICBTdG9wSURfQ2xlYW4sCiAgICAgICAgICBFdmVudF9UaW1lCiAgICAgICAgICkKCnN0cihDb21wYXJlX05UVF9XRCkgICMgMiw4MTAsMTA5IHJvd3Mgb3ZlcmFsbCAgLS0gIDI5LDI2MSByb3dzIHdpdGggbm8gbWF0Y2gKVmlldyhoZWFkKENvbXBhcmVfTlRUX1dELCA1MDApKQpWaWV3KGZpbHRlcihDb21wYXJlX05UVF9XRCwKICAgICAgICAgICAgaXMubmEoTWluVGltZSkKICAgICAgICAgICApCiAgICApCgoKCiMgVmlldyhhbnRpX2pvaW4oU2FtcCwKIyAgICAgICAgICAgICAgICBkaXN0aW5jdChzZWxlY3QoV2FpdERhdGFfUm91dGVQdWxsLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSb3dOdW1fT0cKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiMgICAgICAgICAgICAgICAgICAgICAgICApLAojICAgICAgICAgICAgICAgIGJ5ID0gYygiUm93TnVtX09HIiA9ICJSb3dOdW1fT0ciKQojICAgICAgICAgICAgICAgKQojICAgICApCgojIFRoZSBTYW1wVGltZSBpcyBBRlRFUiB0aGUgbGFzdCBidXMgcGFzc2VkIHRoYXQgU3RvcElEX0NsZWFuCiMgVmlldyhmaWx0ZXIoU2FtcCwKIyAgICAgICAgICAgICAgIFJvdXRlID09ICJYMiIgJgojICAgICAgICAgICAgICAgU3RvcElEX0NsZWFuID09IDEwMDM3NzQKIyAgICAgICAgICAgICAjIFJvd051bV9PRyA9IDExNDY3MjMKIyAgICAgICAgICAgICAjIEV2ZW50X1RpbWUgPSAyMDE2LTEwLTA3IDE1OjMyOjE4CiMgICAgICAgICAgICApCiMgICAgICkKCmBgYAoKCkNsZWFuIHVwIHRoZSBkYXRhIGEgYml0LgpgYGB7cn0KCnJtKEJ1c0dyb3VwcywgUm91dGVNaW5NYXgsIFNhbXAsIFRlc3RpbmcxLCBUZXN0aW5nMiwgVGVzdGluZzMsIFRlc3Rpbmc0LCBUZXN0aW5nNSwgVGVzdGluZ18zLCBUZXN0aW5nXzQsIFRlc3RpbmdfNSwgVGVzdGluZ182LCBUZXN0aW5nXzcsIFdhaXREYXRhX0RheVB1bGwsIFdhaXREYXRhX0RpZmYpCgoKc3RyKENvbXBhcmVfTlRUX1dEKQpWaWV3KGhlYWQoQ29tcGFyZV9OVFRfV0QsIDUwMCkpClZpZXcoaGVhZChtdXRhdGUoQ29tcGFyZV9OVFRfV0QsCiAgICAgICAgICAgICAgICAgV1RfTWluID0gYXMubnVtZXJpYyhXYWl0VGltZV9NaW4yKQogICAgICAgICAgICAgICAgKQogICAgICAgICApCiAgICApCgpXYWl0VGltZV9Bc051bSA8LSBDb21wYXJlX05UVF9XRCAlPiUgCiAgbXV0YXRlKFJvdXRlU3RvcF9JRCA9IGZhY3RvcihwYXN0ZShSb3V0ZSwgU3RvcElEX0NsZWFuLCBzZXAgPSAiX18iKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgKQpXYWl0VGltZV9Bc051bSRXYWl0VGltZV9TZWMyIDwtIGFzLm51bWVyaWMoV2FpdFRpbWVfQXNOdW0kV2FpdFRpbWVfU2VjMikKV2FpdFRpbWVfQXNOdW0kV2FpdFRpbWVfTWluMiA8LSBhcy5udW1lcmljKFdhaXRUaW1lX0FzTnVtJFdhaXRUaW1lX01pbjIpCgpybShDb21wYXJlX05UVF9XRCkKc3RyKFdhaXRUaW1lX0FzTnVtKQoKYGBgCgoKR2VuZXJhbCBleHBsb3JhdGlvbiBvZiB3YWl0IHRpbWVzLgpgYGB7cn0KCnN1bW1hcnkoV2FpdFRpbWVfQXNOdW0kV2FpdFRpbWVfTWluMikKCmBgYAoKCkdlbmVyYWwgZXhwbG9yYXRpb24gb2Ygd2FpdCB0aW1lcy4KYGBge3J9CgpXVF9RdWFudGlsZXMgPC0gYXMuZGF0YS5mcmFtZShxdWFudGlsZShXYWl0VGltZV9Bc051bSRXYWl0VGltZV9NaW4yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9icyA9IHNlcSgwLCAxLCAwLjAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCmNvbG5hbWVzKFdUX1F1YW50aWxlcykgPC0gIlZhbHVlX01pbiIKCldUX1F1YW50aWxlcyRWYWx1ZV9TZWMgPSBmb3JtYXQocm91bmQoV1RfUXVhbnRpbGVzJFZhbHVlX01pbiAqIDYwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpZ2l0cyA9IDIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnNtYWxsID0gMgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQpXVF9RdWFudGlsZXMkVmFsdWVfSHIgPSBmb3JtYXQocm91bmQoV1RfUXVhbnRpbGVzJFZhbHVlX01pbiAvIDYwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZGlnaXRzID0gMgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5zbWFsbCA9IDIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKV1RfUXVhbnRpbGVzJFZhbHVlX01pbiA9IGZvcm1hdChyb3VuZChXVF9RdWFudGlsZXMkVmFsdWVfTWluLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpZ2l0cyA9IDIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnNtYWxsID0gMgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKV1RfUXVhbnRpbGVzJFF1YW50aWxlIDwtIHNlcSgwLCAxLCAwLjAxKQoKV1RfUXVhbnRpbGVzIDwtIHNlbGVjdChXVF9RdWFudGlsZXMsCiAgICAgICAgICAgICAgICAgICAgICAgUXVhbnRpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgVmFsdWVfU2VjLAogICAgICAgICAgICAgICAgICAgICAgIFZhbHVlX01pbiwKICAgICAgICAgICAgICAgICAgICAgICBWYWx1ZV9IcgogICAgICAgICAgICAgICAgICAgICAgKQoKc3RyKFdUX1F1YW50aWxlcykKVmlldyhXVF9RdWFudGlsZXMpCldUX1F1YW50aWxlcwoKClZpZXcoYXJyYW5nZShXYWl0VGltZV9Bc051bSwKICAgICAgICAgICAgIGRlc2MoV2FpdFRpbWVfTWluMikKICAgICAgICAgICAgKSAlPiUgCiAgICAgICBoZWFkKC4sIDUwMDApCiAgICApCgpWaWV3KGZpbHRlcihXYWl0VGltZV9Bc051bSwKICAgICAgICAgICAgYmV0d2VlbihXYWl0VGltZV9NaW4yLCA2MCwgMjAwKQogICAgICAgICAgICkgJT4lIAogICAgICAgYXJyYW5nZShkZXNjKFdhaXRUaW1lX01pbjIpCiAgICAgICAgICAgICAgKSAKICAgICAjICU+JSAKICAgICAjICAgaGVhZCguLCA1MDAwKQogICAgKQoKIyBFeGFtcGxlIG9mIGV4dHJlbWUgd2FpdCB0aW1lcwpWaWV3KGZpbHRlcihXYWl0VGltZV9Bc051bSwKICAgICAgICAgICAgUm91dGUgPT0gIlcxMyIgJiAgIyBvbmx5IDIgYnVzIHBhc3NlcyBpbiB0aGUgZW50aXJlIGRhdGFzZXQKICAgICAgICAgICAgICBTdG9wSURfQ2xlYW4gPT0gMTAwMzcyOAogICAgICAgICAgICAjIEV2ZW50X1RpbWUgPSAyMDE2LTEwLTAzIDA4OjQyOjQ2CiAgICAgICAgICAgKQogICAgKQoKIyBFeGFtcGxlIG9mIGV4dHJlbWUgd2FpdCB0aW1lcwpWaWV3KGZpbHRlcihXYWl0VGltZV9Bc051bSwKICAgICAgICAgICAgUm91dGUgPT0gIlM0MSIgJiAgIyBvbmx5IDQgYnVzIHBhc3NlcyBpbiB0aGUgZW50aXJlIGRhdGFzZXQKICAgICAgICAgICAgICBTdG9wSURfQ2xlYW4gPT0gMTAwMTA5NQogICAgICAgICAgICAjIEV2ZW50X1RpbWUgPSAyMDE2LTEwLTA1IDE1OjQxOjQ3CiAgICAgICAgICAgKQogICAgKQoKIyBFeGFtcGxlIG9mIGV4dHJlbWUgd2FpdCB0aW1lcwpWaWV3KGZpbHRlcihXYWl0VGltZV9Bc051bSwKICAgICAgICAgICAgUm91dGUgPT0gIkQ4IiAmICAjIHJvdXRlIGhhcyBWRVJZIGxpbWl0ZWQgc2VydmljZSBhZnRlciBtaWRuaWdodAogICAgICAgICAgICAgIFN0b3BJRF9DbGVhbiA9PSAxMDAxNjY5CiAgICAgICAgICAgICMgRXZlbnRfVGltZSA9IDIwMTYtMTAtMDYgMjA6MzE6MTYKICAgICAgICAgICApCiAgICApCgpgYGAKCgpMb29rcyBsaWtlIHRoZXJlIG1pZ2h0IGJlIGFuIGlzc3VlIGluIHdhaXQgdGltZXMgd2hlbiB2ZXJ5IGZldyBSb3V0ZS1TdG9wIGNvbWJpbmF0aW9ucyBhcmUgaW5jbHVkZWQgaW4gdGhlIGRhdGFzZXQuICBMZXQncyBleHBsb3JlIHRoZXNlLgpgYGB7cn0KClJvdXRlU3RvcF9DbnRzIDwtIGdyb3VwX2J5KFdhaXRUaW1lX0FzTnVtLAogICAgICAgICAgICAgICAgICAgICAgICAgICBSb3V0ZVN0b3BfSUQKICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzdW1tYXJpc2UoUm91dGVTdG9wX0NudE51bSA9IG4oKSwKICAgICAgICAgICAgUm91dGVTdG9wX0NudFBjdCA9IFJvdXRlU3RvcF9DbnROdW0gLyBucm93KFdhaXRUaW1lX0FzTnVtKQogICAgICAgICAgICkgJT4lIAogIGFycmFuZ2UoUm91dGVTdG9wX0NudE51bSkKClZpZXcoUm91dGVTdG9wX0NudHMpCgoKUm91dGVTdG9wX0NudE9mQ250IDwtIGdyb3VwX2J5KFJvdXRlU3RvcF9DbnRzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUm91dGVTdG9wX0NudE51bQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzdW1tYXJpc2UoUm91dGVTdG9wQ250X0NudE51bSA9IG4oKSwKICAgICAgICAgICAgUm91dGVTdG9wQ250X0NudFBjdCA9IFJvdXRlU3RvcENudF9DbnROdW0gLyBucm93KFJvdXRlU3RvcF9DbnRzKQogICAgICAgICAgICkgJT4lIAogIG11dGF0ZShSb3V0ZVN0b3BDbnRfQ250UGN0X0N1bVN1bSA9IGN1bXN1bShSb3V0ZVN0b3BDbnRfQ250UGN0KSwKICAgICAgICAgeCA9IDEgLSBSb3V0ZVN0b3BDbnRfQ250UGN0X0N1bVN1bQogICAgICAgICkgJT4lIAogIGFycmFuZ2UoUm91dGVTdG9wX0NudE51bSkKICAKIFZpZXcoUm91dGVTdG9wX0NudE9mQ250KQogUm91dGVTdG9wX0NudE9mQ250CgpgYGAKCgpIaXN0b2dyYW0gb2YgdGhlIGNvdW50cyBvZiBSb3V0ZS1TdG9wSUQgY29tYmluYXRpb25zLgpgYGB7cn0KClJvdXRlU3RvcF9DbnRzX0JhciA8LSBnZ3Bsb3QoUm91dGVTdG9wX0NudE9mQ250LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gUm91dGVTdG9wX0NudE51bSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB5ID0gLi5kZW5zaXR5Li4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IFJvdXRlU3RvcENudF9DbnROdW0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICApICsKICAjIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gNSwgZmlsbCA9ICJsaWdodGJsdWUiLCBjb2xvdXIgPSAiZ3JleTYwIiwgc2l6ZSA9IDAuMikgKwogIGdlb21fY29sKGZpbGwgPSAibGlnaHRibHVlIiwgY29sb3VyID0gImdyZXk2MCIsIHNpemUgPSAwLjIpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMCwgNTAwKQogICAgICAgICAgICAgICAgICAjIHlsaW0gPSBjKDAsIDAuMDIpCiAgICAgICAgICAgICAgICAgKSArCiAgbGFicyh0aXRsZSA9ICJWYXJpYXRpb24gaW4gUm91dGVzIFBhc3NpbmcgYSBTcGVjaWZpYyBTdG9wIiwKICAgICAgIHggPSAiT2NjdXJyZW5jZXMgb2YgUm91dGUtU3RvcElEIENvbWJpYW50aW9ucyIsCiAgICAgICB5ID0gIkNvdW50cyIKICAgICAgKQoKUm91dGVTdG9wX0NudHNfQmFyCgpgYGAKCgpDcmVhdGUgYSBuZXcgZGF0YXNldCBsaW1pdGluZyBleHRyZW1lbHkgc21hbGwgY291bnRzIG9mIFJvdXRlLVN0b3BJRCBjb21iaW5hdGlvbnMuCmBgYHtyfQoKV2FpdFRpbWVfUnRlQ250cyA8LSBsZWZ0X2pvaW4oV2FpdFRpbWVfQXNOdW0sCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFJvdXRlU3RvcF9DbnRzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBieSA9IGMoIlJvdXRlU3RvcF9JRCIgPSAiUm91dGVTdG9wX0lEIikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzZWxlY3QoLVJvdXRlU3RvcF9DbnRQY3QpCgpkaW0oV2FpdFRpbWVfQXNOdW0pCmRpbShXYWl0VGltZV9SdGVDbnRzKQoKcm0oV2FpdFRpbWVfQXNOdW0pCnN0cihXYWl0VGltZV9SdGVDbnRzKQoKCiMgVG90YWwgcm93cwpucm93KFdhaXRUaW1lX1J0ZUNudHMpCgojIFJvd3Mgb2YgcmFyZSBSb3V0ZVN0b3BzCm5yb3coZmlsdGVyKFdhaXRUaW1lX1J0ZUNudHMsCiAgICAgICAgICAgIFJvdXRlU3RvcF9DbnROdW0gPD0gNjAKICAgICAgICAgICApCiAgICApIC8gbnJvdyhXYWl0VGltZV9SdGVDbnRzKQoKIyBSb3dzIG9mIGV4dHJlbWVseSBsb25nIHdhaXQgdGltZXMKbnJvdyhmaWx0ZXIoV2FpdFRpbWVfUnRlQ250cywKICAgICAgICAgICAgV2FpdFRpbWVfTWluMiA+IDE4MAogICAgICAgICAgICkKICAgICkgLyBucm93KFdhaXRUaW1lX1J0ZUNudHMpCgoKc2VsZWN0KFdhaXRUaW1lX1J0ZUNudHMsCiAgICAgICBXYWl0VGltZV9NaW4yCiAgICAgICkgJT4lIAogIHN1bW1hcnkoKQoKZmlsdGVyKFdhaXRUaW1lX1J0ZUNudHMsCiAgICAgICBSb3V0ZVN0b3BfQ250TnVtID4gNjAgICMgMTIgcGFzc2VzIHBlciBkYXkgaW4gYSA1LWRheSBkYXRhc2V0CiAgICAgICkgJT4lIAogIHNlbGVjdChXYWl0VGltZV9NaW4yKSAlPiUgCiAgc3VtbWFyeSgpCgpmaWx0ZXIoV2FpdFRpbWVfUnRlQ250cywKICAgICAgIFdhaXRUaW1lX01pbjIgPCAxODAgICMgcHJvYmFibHkgbWVhbnMgdGhhdCBzb21ldGhpbmcgd2VudCB3cm9uZwogICAgICApICU+JSAKICBzZWxlY3QoV2FpdFRpbWVfTWluMikgJT4lIAogIHN1bW1hcnkoKQoKYGBgCgoKQ29tcGFyZSBxdWFudGlsZXMgaW4gdGhlIGxpbWl0ZWQgZGF0YXNldHMuCmBgYHtyfQoKYSA8LSBhcy5kYXRhLmZyYW1lKHNlbGVjdChXYWl0VGltZV9SdGVDbnRzLAogICAgICAgICAgICAgICAgICAgICAgICAgIFdhaXRUaW1lX01pbjIKICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogICAgICAgICAgICAgICAgICAgICBxdWFudGlsZShwcm9icyA9IHNlcSgwLCAxLCAwLjAxKSwgbmEucm0gPSBUUlVFKQogICAgICAgICAgICAgICAgICApCgpiIDwtIGFzLmRhdGEuZnJhbWUoZmlsdGVyKFdhaXRUaW1lX1J0ZUNudHMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgUm91dGVTdG9wX0NudE51bSA+IDYwCiAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KFdhaXRUaW1lX01pbjIpICU+JSAKICAgICAgICAgICAgICAgICAgICAgcXVhbnRpbGUocHJvYnMgPSBzZXEoMCwgMSwgMC4wMSksIG5hLnJtID0gVFJVRSkKICAgICAgICAgICAgICAgICAgKQoKYyA8LSBhcy5kYXRhLmZyYW1lKGZpbHRlcihXYWl0VGltZV9SdGVDbnRzLAogICAgICAgICAgICAgICAgICAgICAgICAgIFdhaXRUaW1lX01pbjIgPCAxODAKICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogICAgICAgICAgICAgICAgICAgICBzZWxlY3QoV2FpdFRpbWVfTWluMikgJT4lIAogICAgICAgICAgICAgICAgICAgICBxdWFudGlsZShwcm9icyA9IHNlcSgwLCAxLCAwLjAxKSwgbmEucm0gPSBUUlVFKQogICAgICAgICAgICAgICAgICApCgpXVF9GaWx0ZXJfUXVhbnRpbGVzIDwtIGJpbmRfY29scyhhLCBiLCBjKSAlPiUgCiAgbXV0YXRlKFF1YW50aWxlID0gc2VxKDAsIDEsIDAuMDEpCiAgICAgICAgKQoKY29sbmFtZXMoV1RfRmlsdGVyX1F1YW50aWxlcykgPC0gYygiQWxsIiwgIlJ0ZVN0cEFidjYwIiwgIldUQmx3MTgwIiwgIlF1YW50aWxlIikKcm0oYSwgYiwgYykKVmlldyhXVF9GaWx0ZXJfUXVhbnRpbGVzKQpXVF9GaWx0ZXJfUXVhbnRpbGVzCgpgYGAKCgpIaXN0b2dyYW0gb2YgYWxsIHdhaXQgdGltZXMuCmBgYHtyfQoKV2FpdFRpbWVfQWxsQnVzX0hpc3REZW4gPC0gZ2dwbG90KGZpbHRlcihzZWxlY3QoV2FpdFRpbWVfUnRlQ250cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgV2FpdFRpbWVfTWluMgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIWlzLm5hKFdhaXRUaW1lX01pbjIpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKHggPSBXYWl0VGltZV9NaW4yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSAuLmRlbnNpdHkuLgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gNSwgZmlsbCA9ICJsaWdodGJsdWUiLCBjb2xvdXIgPSAiZ3JleTYwIiwgc2l6ZSA9IDAuMikgKwogIGdlb21fbGluZShzdGF0ID0gImRlbnNpdHkiLCBjb2xvdXIgPSAicmVkIikgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMzAwLCAzMCkKICAgICAgICAgICAgICAgICAgICApICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMCwgMzAwKSwKICAgICAgICAgICAgICAgICAgeWxpbSA9IGMoMCwgMC4wMzUpCiAgICAgICAgICAgICAgICAgKSArCiAgbGFicyh0aXRsZSA9ICJWYXJpYXRpb24gaW4gV2FpdCBUaW1lIiwKICAgICAgIHggPSAiV2FpdCBUaW1lIChtaW4pIiwKICAgICAgIHkgPSAiRGVuc2l0eSIKICAgICAgKQoKV2FpdFRpbWVfQWxsQnVzX0hpc3REZW4KCmBgYAoKCkJveCBwbG90cyBmb3IgV2FpdFRpbWUgKGFsbCBidXNzZXMsIGJ5IFppcCBDb2RlKS4KYGBge3J9CgojIENvdW50X1ZhbHVlcyBpcyBuZWVkZWQgdG8gZGlzcGxheSB0aGUgbWVkaWFucyBvbiB0aGUgYm94IHBsb3RzCkJ1c1JvdXRlIDwtIHNlbGVjdChXYWl0VGltZV9SdGVDbnRzLAogICAgICAgICAgICAgICAgICAgUm91dGUsCiAgICAgICAgICAgICAgICAgICBXYWl0VGltZV9NaW4yLAogICAgICAgICAgICAgICAgICAgU3RvcF9aaXAKICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgZmlsdGVyKFJvdXRlID09ICJYMiIpCgpDb3VudFZhbHVlc19BbGxCdXNfWmlwIDwtIGRkcGx5KEJ1c1JvdXRlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC4oU3RvcF9aaXApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBWYWx1ZV9Db3VudHMgPSBtZWRpYW4oV2FpdFRpbWVfTWluMiwgbmEucm0gPSBUUlVFKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKV2FpdFRpbWVfQWxsQnVzX1ppcF9Cb3ggPC0gZ2dwbG90KEJ1c1JvdXRlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGZhY3RvcihTdG9wX1ppcCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgV2FpdFRpbWVfTWluMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gZmFjdG9yKFN0b3BfWmlwKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICsgCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuY29sb3VyPSJyZWQiLCBub3RjaD1UUlVFLCBuYS5ybSA9IFRSVUUpICsKICBnZW9tX3RleHQoZGF0YSA9IENvdW50VmFsdWVzX0FsbEJ1c19aaXAsCiAgICAgICAgICAgIGFlcyh5ID0gVmFsdWVfQ291bnRzLAogICAgICAgICAgICAgICAgbGFiZWwgPSBmb3JtYXQocm91bmQoVmFsdWVfQ291bnRzLCBkaWdpdHMgPSAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5zbWFsbCA9IDEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICApLAogICAgICAgICAgICBzaXplID0gMywKICAgICAgICAgICAgdmp1c3QgPSAtMC41CiAgICAgICAgICAgKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUpKSArCiAgY29vcmRfY2FydGVzaWFuKCMgeGxpbSA9IGMoMCwgMTgwKSwKICAgICAgICAgICAgICAgICAgeWxpbSA9IGMoMCwgNDUpCiAgICAgICAgICAgICAgICAgKSArCiAgbGFicyh0aXRsZSA9ICJXYWl0aW5nIFRpbWUgYXQgYSBHaXZlbiBTdG9wIChmb3IgdGhlIFgyKSIsCiAgICAgICB4ID0gIlppcCBDb2RlIG9mIERlc3RpbmF0aW9uIiwKICAgICAgIHkgPSAiV2FpdGluZyBUaW1lIChtaW4pIgogICAgICApCgpXYWl0VGltZV9BbGxCdXNfWmlwX0JveAoKYGBgCgoKVGVzdCBpbnZlc3RpZ2F0aW9uIG9mIGp1c3QgdGhlIFgyIFJvdXRlLiBWaW9saW4gcGxvdHMgZm9yIHRpbWUgYmV0d2VlbiBidXMgYXJyaXZhbHMgKGJ5IFppcCBDb2RlKS4KYGBge3J9CgpXYWl0VGltZV9BbGxCdXNfWmlwX1Zpb2xpbiA8LSBnZ3Bsb3QoQnVzUm91dGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoZmFjdG9yKFN0b3BfWmlwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBXYWl0VGltZV9NaW4yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBmYWN0b3IoU3RvcF9aaXApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgKyAKICBnZW9tX3Zpb2xpbihkcmF3X3F1YW50aWxlcyA9IGMoMC4yNSwgMC41LCAwLjc1KSwKICAgICAgICAgICAgICB0cmltID0gVFJVRSwKICAgICAgICAgICAgICBzY2FsZSA9ICJjb3VudCIsCiAgICAgICAgICAgICAgbmEucm0gPSBUUlVFLAogICAgICAgICAgICAgIHNob3cubGVnZW5kID0gTkEsCiAgICAgICAgICAgICAgaW5oZXJpdC5hZXMgPSBUUlVFCiAgICAgICAgICAgICApICsKICBnZW9tX3RleHQoZGF0YSA9IENvdW50VmFsdWVzX0FsbEJ1c19aaXAsCiAgICAgICAgICAgIGFlcyh5ID0gVmFsdWVfQ291bnRzLAogICAgICAgICAgICAgICAgbGFiZWwgPSBmb3JtYXQocm91bmQoVmFsdWVfQ291bnRzLCBkaWdpdHMgPSAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5zbWFsbCA9IDEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICApLAogICAgICAgICAgICBzaXplID0gMy41LAogICAgICAgICAgICB2anVzdCA9IC0wLjUKICAgICAgICAgICApICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiLCBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT00NSkpICsKICBjb29yZF9jYXJ0ZXNpYW4oIyB4bGltID0gYygwLCAxODApLAogICAgICAgICAgICAgICAgICB5bGltID0gYygwLCA0NSkKICAgICAgICAgICAgICAgICApICsKICBsYWJzKHRpdGxlID0gIldhaXRpbmcgVGltZSBhdCBhIEdpdmVuIFN0b3AgKGZvciB0aGUgWDIpIiwKICAgICAgIHggPSAiWmlwIENvZGUgb2YgRGVzdGluYXRpb24iLAogICAgICAgeSA9ICJXYWl0aW5nIFRpbWUgKG1pbikiCiAgICAgICkKClRpbWVCdHdFdmVudHNfWDJfVmlvbGluUGxvdF96CgpgYGAKCgpCb3ggcGxvdHMgZm9yIFdhaXRUaW1lIChaaXAgQ29kZSwgYnkgSG91ckdyb3VwWmlwKS4KYGBge3J9CgojIENvdW50X1ZhbHVlcyBpcyBuZWVkZWQgdG8gZGlzcGxheSB0aGUgbWVkaWFucyBvbiB0aGUgYm94IHBsb3RzClppcCA8LSBzZWxlY3QoV2FpdFRpbWVfUnRlQ250cywKICAgICAgICAgICAgICBSb3V0ZSwKICAgICAgICAgICAgICBXYWl0VGltZV9NaW4yLAogICAgICAgICAgICAgIFN0b3BfWmlwLAogICAgICAgICAgICAgIEV2ZW50X1RpbWVfSHJHcm91cAogICAgICAgICAgICAgKSAlPiUgCiAgZmlsdGVyKFN0b3BfWmlwID09IDIwMDAyKQoKQ291bnRWYWx1ZXNfQWxsQnVzX0hHIDwtIGRkcGx5KFppcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC4oRXZlbnRfVGltZV9Ickdyb3VwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFZhbHVlX0NvdW50cyA9IG1lZGlhbihXYWl0VGltZV9NaW4yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKV2FpdFRpbWVfQWxsQnVzX0hHX0JveCA8LSBnZ3Bsb3QoWmlwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoZmFjdG9yKEV2ZW50X1RpbWVfSHJHcm91cCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBXYWl0VGltZV9NaW4yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGZhY3RvcihFdmVudF9UaW1lX0hyR3JvdXApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICsgCiAgZ2VvbV9ib3hwbG90KG91dGxpZXIuY29sb3VyPSJyZWQiLCBub3RjaD1UUlVFLCBuYS5ybSA9IFRSVUUpICsKICBnZW9tX3RleHQoZGF0YSA9IENvdW50VmFsdWVzX0FsbEJ1c19IRywKICAgICAgICAgICAgYWVzKHkgPSBWYWx1ZV9Db3VudHMsCiAgICAgICAgICAgICAgICBsYWJlbCA9IGZvcm1hdChyb3VuZChWYWx1ZV9Db3VudHMsIGRpZ2l0cyA9IDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnNtYWxsID0gMQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICksCiAgICAgICAgICAgIHNpemUgPSAyLjUsCiAgICAgICAgICAgIHZqdXN0ID0gLTAuNQogICAgICAgICAgICkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTQ1KSkgKwogIGNvb3JkX2NhcnRlc2lhbigjIHhsaW0gPSBjKDAsIDE4MCksCiAgICAgICAgICAgICAgICAgIHlsaW0gPSBjKDAsIDQ1KQogICAgICAgICAgICAgICAgICkgKwogIGxhYnModGl0bGUgPSAiV2FpdGluZyBUaW1lIGF0IGEgR2l2ZW4gU3RvcCAoZm9yIFppcCAyMDAwMikiLAogICAgICAgeCA9ICJIb3VyIEdyb3VwIiwKICAgICAgIHkgPSAiV2FpdGluZyBUaW1lIChtaW4pIgogICAgICApCiAgIyBmYWNldF93cmFwKH5TdG9wX1ppcAogICMgICAgICAgICAgICAjIG5yb3cgPSA1CiAgIyAgICAgICAgICAgKQoKV2FpdFRpbWVfQWxsQnVzX0hHX0JveAoKYGBgCgoKVmlvbGluIHBsb3RzIGZvciBXYWl0VGltZSAoWmlwIENvZGUsIGJ5IEhvdXJHcm91cFppcCkuCmBgYHtyfQoKV2FpdFRpbWVfQWxsQnVzX0hHX1ZsbiA8LSBnZ3Bsb3QoWmlwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoZmFjdG9yKEV2ZW50X1RpbWVfSHJHcm91cCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBXYWl0VGltZV9NaW4yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGZhY3RvcihFdmVudF9UaW1lX0hyR3JvdXApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICsgCiAgZ2VvbV92aW9saW4oZHJhd19xdWFudGlsZXMgPSBjKDAuMjUsIDAuNSwgMC43NSksCiAgICAgICAgICAgICAgdHJpbSA9IFRSVUUsCiAgICAgICAgICAgICAgc2NhbGUgPSAiY291bnQiLAogICAgICAgICAgICAgIG5hLnJtID0gVFJVRSwKICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IE5BLAogICAgICAgICAgICAgIGluaGVyaXQuYWVzID0gVFJVRQogICAgICAgICAgICAgKSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBDb3VudFZhbHVlc19BbGxCdXNfSEcsCiAgICAgICAgICAgIGFlcyh5ID0gVmFsdWVfQ291bnRzLAogICAgICAgICAgICAgICAgbGFiZWwgPSBmb3JtYXQocm91bmQoVmFsdWVfQ291bnRzLCBkaWdpdHMgPSAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5zbWFsbCA9IDEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICApLAogICAgICAgICAgICBzaXplID0gMi41LAogICAgICAgICAgICB2anVzdCA9IC0wLjUKICAgICAgICAgICApICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiLCBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT00NSkpICsKICBjb29yZF9jYXJ0ZXNpYW4oIyB4bGltID0gYygwLCAxODApLAogICAgICAgICAgICAgICAgICB5bGltID0gYygwLCA5MCkKICAgICAgICAgICAgICAgICApICsKICBsYWJzKHRpdGxlID0gIldhaXRpbmcgVGltZSBhdCBhIEdpdmVuIFN0b3AgKGZvciBaaXAgMjAwMDIpIiwKICAgICAgIHggPSAiSG91ciBHcm91cCIsCiAgICAgICB5ID0gIldhaXRpbmcgVGltZSAobWluKSIKICAgICAgKQogICMgZmFjZXRfd3JhcCh+U3RvcF9aaXAKICAjICAgICAgICAgICAgIyBucm93ID0gNQogICMgICAgICAgICAgICkKCldhaXRUaW1lX0FsbEJ1c19IR19WbG4KCmBgYAoKCkJveCBwbG90cyBmb3IgV2FpdFRpbWUgKFJvdXRlLCBieSBIb3VyR3JvdXBaaXApLgpgYGB7cn0KCiMgQ291bnRfVmFsdWVzIGlzIG5lZWRlZCB0byBkaXNwbGF5IHRoZSBtZWRpYW5zIG9uIHRoZSBib3ggcGxvdHMKUnRlIDwtIHNlbGVjdChXYWl0VGltZV9SdGVDbnRzLAogICAgICAgICAgICAgIFJvdXRlLAogICAgICAgICAgICAgIFdhaXRUaW1lX01pbjIsCiAgICAgICAgICAgICAgU3RvcF9aaXAsCiAgICAgICAgICAgICAgRXZlbnRfVGltZV9Ickdyb3VwCiAgICAgICAgICAgICApICU+JSAKICBmaWx0ZXIoUm91dGUgPT0gIlgyIikKCkNvdW50VmFsdWVzX0FsbEJ1c19SdGVIRyA8LSBncm91cF9ieShSdGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0hyR3JvdXAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgc3VtbWFyaXNlKAogICAgVmFsdWVfQ291bnRzID0gbWVkaWFuKFdhaXRUaW1lX01pbjIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgVkMgPSBxdWFudGlsZShXYWl0VGltZV9NaW4yLCBwcm9icyA9IDAuOSwgbmEucm0gPSBUUlVFKQogICAgKQoKCldhaXRUaW1lX0FsbEJ1c19SdGVIR19Cb3ggPC0gZ2dwbG90KFJ0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGZhY3RvcihFdmVudF9UaW1lX0hyR3JvdXApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgV2FpdFRpbWVfTWluMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBmYWN0b3IoRXZlbnRfVGltZV9Ickdyb3VwKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSArIAogIGdlb21fYm94cGxvdChvdXRsaWVyLmNvbG91cj0icmVkIiwgbm90Y2g9VFJVRSwgbmEucm0gPSBUUlVFKSArCiAgZ2VvbV90ZXh0KGRhdGEgPSBDb3VudFZhbHVlc19BbGxCdXNfUnRlSEcsCiAgICAgICAgICAgIGFlcyh5ID0gVmFsdWVfQ291bnRzLAogICAgICAgICAgICAgICAgbGFiZWwgPSBmb3JtYXQocm91bmQoVmFsdWVfQ291bnRzLCBkaWdpdHMgPSAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5zbWFsbCA9IDEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICApLAogICAgICAgICAgICBzaXplID0gMi41LAogICAgICAgICAgICB2anVzdCA9IC0wLjUKICAgICAgICAgICApICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiLCBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT00NSkpICsKICBjb29yZF9jYXJ0ZXNpYW4oIyB4bGltID0gYygwLCAxODApLAogICAgICAgICAgICAgICAgICB5bGltID0gYygwLCBtYXgoQ291bnRWYWx1ZXNfQWxsQnVzX1J0ZUhHJFZDKSkKICAgICAgICAgICAgICAgICApICsKICBsYWJzKHRpdGxlID0gIldhaXRpbmcgVGltZSBhdCBhIEdpdmVuIFN0b3AiLAogICAgICAgc3VidGl0bGUgPSAoIlJvdXRlIFgyIiksCiAgICAgICB4ID0gIkhvdXIgR3JvdXAiLAogICAgICAgeSA9ICJXYWl0aW5nIFRpbWUgKG1pbikiCiAgICAgICkgCiMgKwojICAgZmFjZXRfd3JhcCh+U3RvcF9aaXAKIyAgICAgICAgICAgICAgIyBucm93ID0gNQojICAgICAgICAgICAgICkKCldhaXRUaW1lX0FsbEJ1c19SdGVIR19Cb3gKCmBgYAoKClZpb2xpbiBwbG90cyBmb3IgV2FpdFRpbWUgKFppcCBDb2RlLCBieSBIb3VyR3JvdXBaaXApLgpgYGB7cn0KCldhaXRUaW1lX0FsbEJ1c19SdGVIR19WbG4gPC0gZ2dwbG90KFJ0ZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWVzKGZhY3RvcihFdmVudF9UaW1lX0hyR3JvdXApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgV2FpdFRpbWVfTWluMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBmYWN0b3IoRXZlbnRfVGltZV9Ickdyb3VwKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSArIAogIGdlb21fdmlvbGluKGRyYXdfcXVhbnRpbGVzID0gYygwLjI1LCAwLjUsIDAuNzUpLAogICAgICAgICAgICAgIHRyaW0gPSBUUlVFLAogICAgICAgICAgICAgIHNjYWxlID0gImNvdW50IiwKICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUsCiAgICAgICAgICAgICAgc2hvdy5sZWdlbmQgPSBOQSwKICAgICAgICAgICAgICBpbmhlcml0LmFlcyA9IFRSVUUKICAgICAgICAgICAgICkgKwogIGdlb21fdGV4dChkYXRhID0gQ291bnRWYWx1ZXNfQWxsQnVzX1J0ZUhHLAogICAgICAgICAgICBhZXMoeSA9IFZhbHVlX0NvdW50cywKICAgICAgICAgICAgICAgIGxhYmVsID0gZm9ybWF0KHJvdW5kKFZhbHVlX0NvdW50cywgZGlnaXRzID0gMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuc21hbGwgPSAxCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgc2l6ZSA9IDIuNSwKICAgICAgICAgICAgdmp1c3QgPSAtMC41CiAgICAgICAgICAgKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUpKSArCiAgY29vcmRfY2FydGVzaWFuKCMgeGxpbSA9IGMoMCwgMTgwKSwKICAgICAgICAgICAgICAgICAgeWxpbSA9IGMoMCwgNDUpCiAgICAgICAgICAgICAgICAgKSArCiAgbGFicyh0aXRsZSA9ICJXYWl0aW5nIFRpbWUgYXQgYSBHaXZlbiBTdG9wIiwKICAgICAgIHN1YnRpdGxlID0gKCIoUm91dGUgWDIpIiksCiAgICAgICB4ID0gIkhvdXIgR3JvdXAiLAogICAgICAgeSA9ICJXYWl0aW5nIFRpbWUgKG1pbikiCiAgICAgICkgKwogIGZhY2V0X3dyYXAoflN0b3BfWmlwCiAgICAgICAgICAgICAjIG5yb3cgPSA1CiAgICAgICAgICAgICkKCldhaXRUaW1lX0FsbEJ1c19SdGVIR19WbG4KCmBgYAoKClgyIFBlcmNlbnRpbGVzIExpbmUgR3JhcGggVGVzdC4KYGBge3J9CgpYMl9QY3QgPC0gc2VsZWN0KFdhaXRUaW1lX1J0ZUNudHMsCiAgICAgICAgICAgICAgICAgUm91dGUsCiAgICAgICAgICAgICAgICAgU3RvcF9aaXAsCiAgICAgICAgICAgICAgICAgRXZlbnRfVGltZV9EYXRlLAogICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfRGF5LAogICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfSHJHcm91cCwKICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0hyLAogICAgICAgICAgICAgICAgIExhdGl0dWRlLAogICAgICAgICAgICAgICAgIExvbmdpdHVkZSwKICAgICAgICAgICAgICAgICBXYWl0VGltZV9NaW4yCiAgICAgICAgICAgICAgICApICU+JSAKICBmaWx0ZXIoUm91dGUgPT0gIlgyIikgJT4lIAogIGdyb3VwX2J5KEV2ZW50X1RpbWVfSHIsCiAgICAgICAgICAgU3RvcF9aaXAKICAgICAgICAgICkgJT4lIAogIHN1bW1hcmlzZShQY3Q1MCA9IHF1YW50aWxlKFdhaXRUaW1lX01pbjIsIHByb2JzID0gMC41LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBQY3Q2MCA9IHF1YW50aWxlKFdhaXRUaW1lX01pbjIsIHByb2JzID0gMC42LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBQY3Q3MCA9IHF1YW50aWxlKFdhaXRUaW1lX01pbjIsIHByb2JzID0gMC43LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBQY3Q4MCA9IHF1YW50aWxlKFdhaXRUaW1lX01pbjIsIHByb2JzID0gMC44LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBQY3Q5MCA9IHF1YW50aWxlKFdhaXRUaW1lX01pbjIsIHByb2JzID0gMC45LCBuYS5ybSA9IFRSVUUpCiAgICAgICAgICAgKQoKc3RyKFgyX1BjdCkKVmlldyhYMl9QY3QpCgoKWDJfTG9uZyA8LSBnYXRoZXIoWDJfUGN0LAogICAgICAgICAgICAgICAgICBrZXkgPSBQZXJjZW50aWxlLAogICAgICAgICAgICAgICAgICB2YWx1ZSA9IFBjdGlsZSwKICAgICAgICAgICAgICAgICAgUGN0NTAsCiAgICAgICAgICAgICAgICAgIFBjdDYwLAogICAgICAgICAgICAgICAgICBQY3Q3MCwKICAgICAgICAgICAgICAgICAgUGN0ODAsCiAgICAgICAgICAgICAgICAgIFBjdDkwCiAgICAgICAgICAgICAgICApCgpzdHIoWDJfTG9uZykKVmlldyhYMl9Mb25nKQoKClgyX1dhaXRCeUhyX0xpbmUgPC0gZ2dwbG90KFgyX0xvbmcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gRXZlbnRfVGltZV9IciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBQY3RpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWN0b3IoUGVyY2VudGlsZSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IFBlcmNlbnRpbGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICkgKwogIGdlb21fbGluZSgpICsKICB0aGVtZShsZWdlbmQudGl0bGU9ZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iCiAgICAgICApICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMCwgMjMpCiAgICAgICAgICAgICAgICAgICMgeWxpbSA9IGMoMCwgNDUpCiAgICAgICAgICAgICAgICAgKSArIAogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMjMsIDIpCiAgICAgICAgICAgICAgICAgICAgKSArCiAgbGFicyh0aXRsZSA9ICJXYWl0aW5nIFRpbWUgVGhyb3VnaG91dCB0aGUgRGF5IiwKICAgICAgIHN1YnRpdGxlID0gKCIoUm91dGUgWDIpIiksCiAgICAgICB4ID0gIkhvdXIgb2YgdGhlIERheSIsCiAgICAgICB5ID0gIldhaXRpbmcgVGltZSAobWluKSIKICAgICAgKSArCiAgZmFjZXRfd3JhcCh+U3RvcF9aaXApCgpYMl9XYWl0QnlIcl9MaW5lCgpgYGAKCgoKR0VUIERBVEEgUkVBRFkgRk9SIFNISU5ZICAtLSAgR0VUIERBVEEgUkVBRFkgRk9SIFNISU5ZICAtLSAgR0VUIERBVEEgUkVBRFkgRk9SIFNISU5ZCkdFVCBEQVRBIFJFQURZIEZPUiBTSElOWSAgLS0gIEdFVCBEQVRBIFJFQURZIEZPUiBTSElOWSAgLS0gIEdFVCBEQVRBIFJFQURZIEZPUiBTSElOWQpHRVQgREFUQSBSRUFEWSBGT1IgU0hJTlkgIC0tICBHRVQgREFUQSBSRUFEWSBGT1IgU0hJTlkgIC0tICBHRVQgREFUQSBSRUFEWSBGT1IgU0hJTlkKCkJhc2VEYXRhOiBVc2VkIGluIHBsb3RzIGJ5IGhvdXIgYW5kIHppcGNvZGUgKGZpcnN0IHR3byBTaGlueSB0YWJzKS4KYGBge3J9CgojIHN0cihXYWl0VGltZV9SdGVDbnRzKQoKU2hpbnlfV2FpdERhdGFfQmFzZSA8LSBzZWxlY3QoV2FpdFRpbWVfUnRlQ250cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUm91dGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN0b3BfWmlwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0RhdGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfRGF5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0hyR3JvdXAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfSHIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIExhdGl0dWRlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBMb25naXR1ZGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFdhaXRUaW1lX01pbjIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBtdXRhdGUoRXZlbnRfVGltZV9Zck10aERheUhyID0gZmxvb3JfZGF0ZShFdmVudF9UaW1lLCAiaG91ciIpCiAgICAgICAgKSAlPiUgCiAgcmVuYW1lKFppcENvZGUgPSBTdG9wX1ppcCwKICAgICAgICAgSG91ckdyb3VwID0gRXZlbnRfVGltZV9Ickdyb3VwLAogICAgICAgICBEYXRlID0gRXZlbnRfVGltZV9EYXRlLAogICAgICAgICBEYXkgPSBFdmVudF9UaW1lX0RheSwKICAgICAgICAgSG91ciA9IEV2ZW50X1RpbWVfSHIsCiAgICAgICAgIFdhaXRUaW1lX01pbiA9IFdhaXRUaW1lX01pbjIKICAgICAgICApICU+JSAKICBmaWx0ZXIoV2FpdFRpbWVfTWluIDw9IDE4MCkKClNoaW55X1dhaXREYXRhX0Jhc2UkUm91dGUgPC0gZmFjdG9yKFNoaW55X1dhaXREYXRhX0Jhc2UkUm91dGUpCgpzdHIoU2hpbnlfV2FpdERhdGFfQmFzZSkKVmlldyh0YWlsKFNoaW55X1dhaXREYXRhX0Jhc2UsIDUwMCkpCgpzYXZlUkRTKFNoaW55X1dhaXREYXRhX0Jhc2UsCiAgICAgICAgIlNoaW55X1dhaXREYXRhX0Jhc2UucmRzIgogICAgICAgKQoKYGBgCgoKUHJlcCBkYXRhIGZvciBtYXBwaW5nLgpgYGB7cn0KCiMgZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJka2FobGUvZ2dtYXAiKQojIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigiaGFkbGV5L2dncGxvdDIiKQojIGluc3RhbGwucGFja2FnZXMoImdnbWFwIiwgdHlwZSA9ICJzb3VyY2UiKQoKIyBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoJ2hhZGxleS9nZ3Bsb3QyJykKZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJoYWRsZXkvZ2dwbG90MkB2Mi4yLjAiKQojIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigndGhvbWFzcDg1L2dnZm9yY2UnKQojIGRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigndGhvbWFzcDg1L2dncmFwaCcpCiMgZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCdzbG93a293L2dncmVwZWwnKQoKCnRyYWN0IDwtIAogIHJlYWRPR1IoZHNuID0gIi9Vc2Vycy9tZHR1cnNlL0Rlc2t0b3AvQW5hbHl0aWNzL0RDTWV0cm9CdXMvdGxfMjAxNl91c196Y3RhNTEwIiwKICAgICAgICAgIGxheWVyID0gInRsXzIwMTZfdXNfemN0YTUxMCIKICAgICAgICAgKQogIApjbGFzcyh0cmFjdCkKCiMgY29udmVydCB0aGUgR0VPSUQgdG8gYSBjaGFyYWN0ZXIKdHJhY3RAZGF0YSRHRU9JRCA8LSBhcy5jaGFyYWN0ZXIodHJhY3RAZGF0YSRHRU9JRCkKc3RyKHRyYWN0QGRhdGEpCgoKZ2d0cmFjdCA8LSB0aWR5KHRyYWN0LCByZWdpb24gPSAiR0VPSUQiKQoKIyBzdHIoZ2d0cmFjdCkKIyBzdW1tYXJ5KGdndHJhY3QpCiMgVmlldyhoZWFkKGdndHJhY3QsIDUwKSkKCgoKIyBzdHIoU2hpbnlfV2FpdERhdGFfQmFzZSkKClppcFdhaXRUZXN0IDwtIGZpbHRlcihTaGlueV9XYWl0RGF0YV9CYXNlLAogICAgICAgICAgICAgICAgICAgICAgV2FpdFRpbWVfTWluIDw9IDE4MCAmCiAgICAgICAgICAgICAgICAgICAgICAgICFpcy5uYShaaXBDb2RlKQogICAgICAgICAgICAgICAgICAgICApICU+JSAKICBncm91cF9ieShaaXBDb2RlLAogICAgICAgICAgIEV2ZW50X1RpbWVfWXJNdGhEYXlIcgogICAgICAgICAgICMgRXZlbnRfVGltZV9EYXksCiAgICAgICAgICAgIyBFdmVudF9UaW1lX0hyCiAgICAgICAgICApICU+JSAKICBzdW1tYXJpc2UoUGN0ODAgPSBxdWFudGlsZShXYWl0VGltZV9NaW4sIHByb2JzID0gMC44LCBuYS5ybSA9IFRSVUUpCiAgICAgICAgICAgKSAlPiUgCiAgYXJyYW5nZSgjIEV2ZW50X1RpbWVfSHIsCiAgICAgICAgICBaaXBDb2RlLAogICAgICAgICAgRXZlbnRfVGltZV9Zck10aERheUhyCiAgICAgICAgICkgJT4lIAogIGFzLmRhdGEuZnJhbWUoKSAlPiUgCiAgbXV0YXRlKEV2ZW50X1RpbWVfRGF0ZU5ldyA9IGZsb29yX2RhdGUoRXZlbnRfVGltZV9Zck10aERheUhyLCAiZGF5IiksCiAgICAgICAgIEV2ZW50X1RpbWVfSHJOZXcgPSBob3VyKEV2ZW50X1RpbWVfWXJNdGhEYXlIciksCiAgICAgICAgIFBjdDgwX0xldmVsID0gZmFjdG9yKGlmZWxzZShQY3Q4MCA8IDEwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJlbG93IDEwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFBjdDgwIDwgMjAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQmVsb3cgMjAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoUGN0ODAgPCAzMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJCZWxvdyAzMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShQY3Q4MCA8IDQwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkJlbG93IDQwIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFBjdDgwIDwgNTAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQmVsb3cgNTAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoUGN0ODAgPCA2MCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJCZWxvdyA2MCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiNjAgUGx1cyIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSkpKSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCJCZWxvdyAxMCIsICJCZWxvdyAyMCIsICJCZWxvdyAzMCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJCZWxvdyA0MCIsICJCZWxvdyA1MCIsICJCZWxvdyA2MCIsICI2MCBQbHVzIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb3JkZXJlZCA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgKQoKc3RyKFppcFdhaXRUZXN0KQpaaXBXYWl0VGVzdCRaaXBDb2RlIDwtIGFzLmNoYXJhY3RlcihaaXBXYWl0VGVzdCRaaXBDb2RlKQpzdHIoWmlwV2FpdFRlc3QpCnN1bW1hcnkoWmlwV2FpdFRlc3QpCgpWaWV3KGhlYWQoWmlwV2FpdFRlc3QsIDUwMCkpCgoKU3RvcFppcF9MZWZ0IDwtIGxlZnRfam9pbihaaXBXYWl0VGVzdCwKICAgICAgICAgICAgICAgICAgICAgICAgICBnZ3RyYWN0LAogICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gYygiWmlwQ29kZSIgPSAiaWQiKQogICAgICAgICAgICAgICAgICAgICAgICAgKQoKc3RyKFN0b3BaaXBfTGVmdCkKc3VtbWFyeShTdG9wWmlwX0xlZnQpCgpgYGAKCgpUZXN0IG1hcHBpbmcgZnVuY3Rpb25hbHRpeS4KYGBge3J9CgptYXAgPC0gZ2V0X21hcChsb2NhdGlvbiA9IGMobG9uID0gLTc3LjAzNjc2LCBsYXQgPSAzOC44OTc4NCksCiAgICAgICAgICAgICAgIHNvdXJjZSA9ICJnb29nbGUiLAogICAgICAgICAgICAgICAjIG1hcHR5cGUgPSAicm9hZG1hcCIKICAgICAgICAgICAgICAgem9vbSA9IDEyCiAgICAgICAgICAgICAgKQoKZ2dtYXAobWFwKSArCiAgZ2VvbV9wb2x5Z29uKGFlcyh4ID0gbG9uZywgCiAgICAgICAgICAgICAgICAgICB5ID0gbGF0LCAKICAgICAgICAgICAgICAgICAgIGdyb3VwID0gZ3JvdXAsCiAgICAgICAgICAgICAgICAgICBmaWxsID0gUGN0ODBfTGV2ZWwKICAgICAgICAgICAgICAgICAgKSwgCiAgICAgICAgICAgICAgIGRhdGEgPSBmaWx0ZXIoU3RvcFppcF9MZWZ0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfWXJNdGhEYXlIciA9PSBhcy5QT1NJWGN0KCIyMDE2LTEwLTA3IDIwOjAwOjAwIikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjICAgU3RvcF9aaXAgPT0gIjIwMDAzIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgY29sb3VyID0gImdyYXkxIiwgCiAgICAgICAgICAgICAgICMgZmlsbCA9ICdibGFjaycsIAogICAgICAgICAgICAgICBhbHBoYSA9IC40LCAKICAgICAgICAgICAgICAgc2l6ZSA9IC4zCiAgICAgICAgICAgICAgKSArCiMgKwogICMgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3VycyA9IGMoIndoaXRlIiwgInJveWFsYmx1ZTQiLCAicmVkIiksCiAgIyAgICAgICAgICAgICAgICAgICAgICAjICAibGlnaHRzdGVlbGJsdWU0IiwKICAjICAgICAgICAgICAgICAgICAgICAgICMgImxpZ2h0cGluazEiLAogICMgICAgICAgICAgICAgICAgICAgICAgIyB2YWx1ZXM9Y2JQYWxldHRlLAogICMgICAgICAgICAgICAgICAgICAgICAgIyB2YWx1ZXMgPSBjKDEsMC41LCAuMywgLjIsIC4xLCAwKQogICMgICAgICAgICAgICAgICAgICAgICAgbmEudmFsdWUgPSAiYmxhY2siLAogICMgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYyhzZXEoMCwgMTgwLCAzMCkpCiAgIyAgICAgICAgICAgICAgICAgICAgICAjIHZhbHVlcyA9IHJlc2NhbGUoKQogICMgICAgICAgICAgICAgICAgICAgICApIAojICsKICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNwZWN0cmFsIiwgIyAiWWxPclJkIiAjICJTZXQxIiwKICAgICAgICAgICAgICAgICAgICBkaXJlY3Rpb24gPSAtMSwKICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBsZXZlbHMoU3RvcFppcF9MZWZ0JFBjdDgwX0xldmVsKQogICAgICAgICAgICAgICAgICAgKQoKYGBgCgoKU2hpbnkgZGF0YSBmb3IgbWFwcGluZyAodXNlZCBpbiAzcmQgdGFiKS4KYGBge3J9CgpWaWV3KGhlYWQoZmlsdGVyKFN0b3BaaXBfTGVmdCwKICAgICAgICAgICAgICAgICBFdmVudF9UaW1lX0hyTmV3ID09IDE1CiAgICAgICAgICAgICAgICApLAogICAgICAgICAgNTAwCiAgICAgICAgICkKICAgICkKClNoaW55X1dhaXREYXRhX01hcCA8LSBTdG9wWmlwX0xlZnQgJT4lIAogIHJlbmFtZShZck10aERheUhyID0gRXZlbnRfVGltZV9Zck10aERheUhyLAogICAgICAgICBZck10aERheSA9IEV2ZW50X1RpbWVfRGF0ZU5ldywKICAgICAgICAgSG91ciA9IEV2ZW50X1RpbWVfSHJOZXcKICAgICAgICApCgpzdHIoU2hpbnlfV2FpdERhdGFfTWFwKQoKClNoaW55X1dhaXREYXRhX01hcF9XZWQgPC0gZmlsdGVyKFNoaW55X1dhaXREYXRhX01hcCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgWXJNdGhEYXkgPT0gYXMuUE9TSVhjdCgiMjAxNi0xMC0wNSIpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKc3RyKFNoaW55X1dhaXREYXRhX01hcF9XZWQpCnN1bW1hcnkoU2hpbnlfV2FpdERhdGFfTWFwX1dlZCkKCgpzYXZlUkRTKFNoaW55X1dhaXREYXRhX01hcCwKICAgICAgICAiU2hpbnlfV2FpdERhdGFfTWFwLnJkcyIKICAgICAgICkKCnNhdmVSRFMoU2hpbnlfV2FpdERhdGFfTWFwX1dlZCwKICAgICAgICAiU2hpbnlfV2FpdERhdGFfTWFwX1dlZC5yZHMiCiAgICAgICApCgpgYGAKCgoKCkNsdXN0ZXJpbmcKCkRhdGEgcHJlcC4KYGBge3J9CgpybSh0cmFjdCwgZ2d0cmFjdCwgU3RvcFppcF9MZWZ0LCBaaXBXYWl0VGVzdCwgU2hpbnlfV2FpdERhdGFfQmFzZSwgU2hpbnlfV2FpdERhdGFfTWFwLCBTaGlueV9XYWl0RGF0YV9NYXBfV2VkKQoKCmRpbShOZXdUcmF2VGltZSkKZGltKFdhaXRUaW1lX1J0ZUNudHMpCgoKc3RyKHNlbGVjdChOZXdUcmF2VGltZSwKICAgICAgICAgICAtbWF0Y2hlcygiKHEoMnw1fCg5NSl8KDk4KSkpfE1lYW58TWVkfENudCIpCiAgICAgICAgICApCiAgICkKc3RyKHNlbGVjdChOZXdUcmF2VGltZSwKICAgICAgICAgICBtYXRjaGVzKCIocSgyfDV8KDk1KXwoOTgpKSl8TWVhbnxNZWR8Q250IikKICAgICAgICAgICkKICAgKQpzdHIoV2FpdFRpbWVfUnRlQ250cykKCiAKIAojIENsdXN0RGF0YSA8LSBzZWxlY3QoV2FpdFRpbWVfUnRlQ250cywKIyAgICAgICAgICAgICAgICAgICAgIGdyb3VwLAojICAgICAgICAgICAgICAgICAgICAgQnVzRGF5X0V2ZW50TnVtLAojICAgICAgICAgICAgICAgICAgICAgUm91dGUsCiMgICAgICAgICAgICAgICAgICAgICBSdGVDaGFuZ2UyLAojICAgICAgICAgICAgICAgICAgICAgUm91dGVBbHQsCiMgICAgICAgICAgICAgICAgICAgICBEaXJDaGFuZ2UyLAojICAgICAgICAgICAgICAgICAgICAgUm91dGVfRGlyZWN0aW9uLAojICAgICAgICAgICAgICAgICAgICAgU3RvcF9TZXF1ZW5jZSwKIyAgICAgICAgICAgICAgICAgICAgIFN0b3BJRF9JbmRpY2F0b3IsCiMgICAgICAgICAgICAgICAgICAgICBTdG9wX0NvdW50eSwKIyAgICAgICAgICAgICAgICAgICAgIFN0b3BfQ2l0eSwKIyAgICAgICAgICAgICAgICAgICAgIFN0b3BfWmlwLAojICAgICAgICAgICAgICAgICAgICAgRXZlbnRfVGltZV9IciwKIyAgICAgICAgICAgICAgICAgICAgIER3ZWxsX1RpbWUyLAojICAgICAgICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycywKIyAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnNfTGFiZWwsCiMgICAgICAgICAgICAgICAgICAgICBUVF9TZWNfTmV3LAojICAgICAgICAgICAgICAgICAgICAgVFRfU2VjX05ld19MYWJlbCwKIyAgICAgICAgICAgICAgICAgICAgIFdhaXRUaW1lX01pbjIKIyAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiMgICBmaWx0ZXIoV2FpdFRpbWVfTWluMiA8PSAxODApICU+JSAKIyAgIG11dGF0ZShTcGVlZEF2Z19NcGhfVERNTkhfVFRTTiA9IFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMgLyAKIyAgICAgICAgICAgIChUVF9TZWNfTmV3IC8gNjAgLyA2MCkKIyAgICAgICAgICkKIyAlPiUgCiMgICBzZWxlY3RfaWYoQ2x1c3REYXRhLAojICAgICAgICAgICAgIGZ1bmN0aW9uKGNvbCkgaXMubnVtZXJpYyhjb2wpIHwKIyAgICAgICAgICAgICAgIGlzLmludGVnZXIoY29sKQojICAgICAgICAgICAgKSAlPiUgCiMgICAgc2NhbGUoKQoKIyBzdHIoQ2x1c3REYXRhKQojIFZpZXcodGFpbChDbHVzdERhdGEsIDUwMCkpCiMgcm93bmFtZXMoQ2x1c3REYXRhKSA8LSBDbHVzdERhdGEkUm91dGUKIyBDbHVzdERhdGEkUm91dGUgPC0gYXMuZmFjdG9yKENsdXN0RGF0YSRSb3V0ZSkKIyBzdHIoQ2x1c3REYXRhKQojIGhlYWQoQ2x1c3REYXRhKQoKClJvdXRlU3RhdHMgPC0gZmlsdGVyKFdhaXRUaW1lX1J0ZUNudHMsCiAgICAgICAgICAgICAgICAgICAgIFdhaXRUaW1lX01pbjIgPD0gMTgwCiAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgbXV0YXRlKFNwZWVkQXZnX01waF9URE1OSF9UVFNOID0gVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycyAvIChUVF9TZWNfTmV3IC8gNjAgLyA2MCkKICAgICAgICApICU+JSAKICBncm91cF9ieShSb3V0ZSkgJT4lIAogIHN1bW1hcmlzZShCdXNEYXlFdmVudE51bV9NZWFuID0gbWVhbihCdXNEYXlfRXZlbnROdW0sIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIEJ1c0RheUV2ZW50TnVtX1BjdDEwID0gcXVhbnRpbGUoQnVzRGF5X0V2ZW50TnVtLCBwcm9icyA9IDAuMTAsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIEJ1c0RheUV2ZW50TnVtX1BjdDI1ID0gcXVhbnRpbGUoQnVzRGF5X0V2ZW50TnVtLCBwcm9icyA9IDAuMjUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIEJ1c0RheUV2ZW50TnVtX1BjdDUwID0gcXVhbnRpbGUoQnVzRGF5X0V2ZW50TnVtLCBwcm9icyA9IDAuNTAsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIEJ1c0RheUV2ZW50TnVtX1BjdDc1ID0gcXVhbnRpbGUoQnVzRGF5X0V2ZW50TnVtLCBwcm9icyA9IDAuNzUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIEJ1c0RheUV2ZW50TnVtX1BjdDkwID0gcXVhbnRpbGUoQnVzRGF5X0V2ZW50TnVtLCBwcm9icyA9IDAuOTAsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIFN0b3BTZXF1ZW5jZV9NZWFuID0gbWVhbihTdG9wX1NlcXVlbmNlLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBTdG9wU2VxdWVuY2VfUGN0MTAgPSBxdWFudGlsZShTdG9wX1NlcXVlbmNlLCBwcm9icyA9IDAuMTAsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIFN0b3BTZXF1ZW5jZV9QY3QyNSA9IHF1YW50aWxlKFN0b3BfU2VxdWVuY2UsIHByb2JzID0gMC4yNSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgU3RvcFNlcXVlbmNlX1BjdDUwID0gcXVhbnRpbGUoU3RvcF9TZXF1ZW5jZSwgcHJvYnMgPSAwLjUwLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBTdG9wU2VxdWVuY2VfUGN0NzUgPSBxdWFudGlsZShTdG9wX1NlcXVlbmNlLCBwcm9icyA9IDAuNzUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIFN0b3BTZXF1ZW5jZV9QY3Q5MCA9IHF1YW50aWxlKFN0b3BfU2VxdWVuY2UsIHByb2JzID0gMC45MCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgRXZlbnRUaW1lSHJfTWVhbiA9IG1lYW4oRXZlbnRfVGltZV9IciwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgRXZlbnRUaW1lSHJfUGN0MTAgPSBxdWFudGlsZShFdmVudF9UaW1lX0hyLCBwcm9icyA9IDAuMTAsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIEV2ZW50VGltZUhyX1BjdDI1ID0gcXVhbnRpbGUoRXZlbnRfVGltZV9IciwgcHJvYnMgPSAwLjI1LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBFdmVudFRpbWVIcl9QY3Q1MCA9IHF1YW50aWxlKEV2ZW50X1RpbWVfSHIsIHByb2JzID0gMC41MCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgRXZlbnRUaW1lSHJfUGN0NzUgPSBxdWFudGlsZShFdmVudF9UaW1lX0hyLCBwcm9icyA9IDAuNzUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIEV2ZW50VGltZUhyX1BjdDkwID0gcXVhbnRpbGUoRXZlbnRfVGltZV9IciwgcHJvYnMgPSAwLjkwLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBEd2VsbFRpbWUyX01lYW4gPSBtZWFuKER3ZWxsX1RpbWUyLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBEd2VsbFRpbWUyX1BjdDEwID0gcXVhbnRpbGUoRHdlbGxfVGltZTIsIHByb2JzID0gMC4xMCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgRHdlbGxUaW1lMl9QY3QyNSA9IHF1YW50aWxlKER3ZWxsX1RpbWUyLCBwcm9icyA9IDAuMjUsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIER3ZWxsVGltZTJfUGN0NTAgPSBxdWFudGlsZShEd2VsbF9UaW1lMiwgcHJvYnMgPSAwLjUwLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBEd2VsbFRpbWUyX1BjdDc1ID0gcXVhbnRpbGUoRHdlbGxfVGltZTIsIHByb2JzID0gMC43NSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgRHdlbGxUaW1lMl9QY3Q5MCA9IHF1YW50aWxlKER3ZWxsX1RpbWUyLCBwcm9icyA9IDAuOTAsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIFRyYXZEaXN0TWlfTWVhbiA9IG1lYW4oVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycywgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgVHJhdkRpc3RNaV9QY3QxMCA9IHF1YW50aWxlKFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9icyA9IDAuMTAsIG5hLnJtID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICBUcmF2RGlzdE1pX1BjdDI1ID0gcXVhbnRpbGUoVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2JzID0gMC4yNSwgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgIFRyYXZEaXN0TWlfUGN0NTAgPSBxdWFudGlsZShUcmF2ZWxEaXN0YW5jZV9NaV9OZXdIdnJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvYnMgPSAwLjUwLCBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgVHJhdkRpc3RNaV9QY3Q3NSA9IHF1YW50aWxlKFRyYXZlbERpc3RhbmNlX01pX05ld0h2cnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9icyA9IDAuNzUsIG5hLnJtID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICBUcmF2RGlzdE1pX1BjdDkwID0gcXVhbnRpbGUoVHJhdmVsRGlzdGFuY2VfTWlfTmV3SHZycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHByb2JzID0gMC45MCwgbmEucm0gPSBUUlVFCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgIFRyYXZUaW1TZWNfTWVhbiA9IG1lYW4oVFRfU2VjX05ldywgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgVHJhdlRpbVNlY19QY3QxMCA9IHF1YW50aWxlKFRUX1NlY19OZXcsIHByb2JzID0gMC4xMCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgVHJhdlRpbVNlY19QY3QyNSA9IHF1YW50aWxlKFRUX1NlY19OZXcsIHByb2JzID0gMC4yNSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgVHJhdlRpbVNlY19QY3Q1MCA9IHF1YW50aWxlKFRUX1NlY19OZXcsIHByb2JzID0gMC41MCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgVHJhdlRpbVNlY19QY3Q3NSA9IHF1YW50aWxlKFRUX1NlY19OZXcsIHByb2JzID0gMC43NSwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgVHJhdlRpbVNlY19QY3Q5MCA9IHF1YW50aWxlKFRUX1NlY19OZXcsIHByb2JzID0gMC45MCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgV2FpdFRpbU1pbl9NZWFuID0gbWVhbihXYWl0VGltZV9NaW4yLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBXYWl0VGltTWluX1BjdDEwID0gcXVhbnRpbGUoV2FpdFRpbWVfTWluMiwgcHJvYnMgPSAwLjEwLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBXYWl0VGltTWluX1BjdDI1ID0gcXVhbnRpbGUoV2FpdFRpbWVfTWluMiwgcHJvYnMgPSAwLjI1LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBXYWl0VGltTWluX1BjdDUwID0gcXVhbnRpbGUoV2FpdFRpbWVfTWluMiwgcHJvYnMgPSAwLjUwLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBXYWl0VGltTWluX1BjdDc1ID0gcXVhbnRpbGUoV2FpdFRpbWVfTWluMiwgcHJvYnMgPSAwLjc1LCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICBXYWl0VGltTWluX1BjdDkwID0gcXVhbnRpbGUoV2FpdFRpbWVfTWluMiwgcHJvYnMgPSAwLjkwLCBuYS5ybSA9IFRSVUUpCiAgICAgICAgICAgKSAlPiUgCiAgYXMuZGF0YS5mcmFtZSgpCgpzdHIoUm91dGVTdGF0cykKCnJvd25hbWVzKFJvdXRlU3RhdHMpIDwtIFJvdXRlU3RhdHMkUm91dGUKc3RyKFJvdXRlU3RhdHMpClZpZXcoUm91dGVTdGF0cykKCgpSb3V0ZVN0YXRzX1NjYWxlZCA8LSBzZWxlY3QoUm91dGVTdGF0cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgIC1Sb3V0ZQogICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzY2FsZSgpCgpzdHIoUm91dGVTdGF0c19TY2FsZWQpCmNsYXNzKFJvdXRlU3RhdHNfU2NhbGVkKQpWaWV3KFJvdXRlU3RhdHNfU2NhbGVkKQoKc3VtbWFyeShSb3V0ZVN0YXRzKQpzdW1tYXJ5KFJvdXRlU3RhdHNfU2NhbGVkKQoKYGBgCgoKUENBCgpVc2luZyBjYXJldDo6cHJlUHJvY2Vzcy4KYGBge3J9CgpzdHIoUm91dGVTdGF0cykKClRybnNmcm0gPC0gcHJlUHJvY2VzcyhzZWxlY3QoUm91dGVTdGF0cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAtUm91dGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICBtZXRob2QgPSBjKCJCb3hDb3giLCAiY2VudGVyIiwgInNjYWxlIiwgInBjYSIpCiAgICAgICAgICAgICAgICAgICAgICkKCiMgbG9hZGluZ3MKVHJuc2ZybSRyb3RhdGlvbgoKUm91dGVTdGF0c19QY2EgPC0gcHJlZGljdChUcm5zZnJtLCBSb3V0ZVN0YXRzKSAlPiUgCiAgc2VsZWN0KC1Sb3V0ZSkKCnN0cihSb3V0ZVN0YXRzX1BjYSkKVmlldyhSb3V0ZVN0YXRzX1BjYSkKaGVhZChSb3V0ZVN0YXRzX1BjYSkKCmBgYAoKClBDQQoKVXNpbmcgc3RhdHM6OnByY29tcC4KYGBge3J9CgpzdHIoUm91dGVTdGF0cykKClBjYVJlcyA8LSBwcmNvbXAoc2VsZWN0KFJvdXRlU3RhdHMsCiAgICAgICAgICAgICAgICAgICAgICAgIC1Sb3V0ZQogICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgY2VudGVyID0gVFJVRSwKICAgICAgICAgICAgICAgICBzY2FsZS4gPSBUUlVFCiAgICAgICAgICAgICAgICApCgpzdHIoUGNhUmVzKQoKaGVhZCh1bmNsYXNzKFBjYVJlcyRyb3RhdGlvbikpCgoKUGNhUmVzX1ZhcnMgPC0gZ2V0X3BjYV92YXIoUGNhUmVzKQpQY2FSZXNfVmFycwojIFdoZXJlIHZhcmlhYmxlcyBsaWUgaW4gcmVsYXRpb24gdG8gdGhlIGVpZ2VudmVjdG9ycwpQY2FSZXNfVmFycyRjb29yZAoKIyBHcmFwaCBvZiB0aGUgRmFjdG9yLVZhcmlhYmxlIE1hcApmdml6X3BjYV92YXIoUGNhUmVzLAogICAgICAgICAgICAgY29sLnZhciA9ICJjb250cmliIgogICAgICAgICAgICApICsKICBzY2FsZV9jb2xvcl9ncmFkaWVudDIobG93ID0gIndoaXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgbWlkID0gImJsdWUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgaGlnaCA9ICJyZWQiLAogICAgICAgICAgICAgICAgICAgICAgICBtaWRwb2ludCA9IDIKICAgICAgICAgICAgICAgICAgICAgICApCgojIEdyYXBoIG9mIHRoZSBGYWN0b3ItVmFyaWFibGUgTWFwICh0b3AgMTAgY29udHJpYnV0aW5nIHZhcmlhYmxlcykKZnZpel9wY2FfdmFyKFBjYVJlcywKICAgICAgICAgICAgIGNvbC52YXIgPSAiY29udHJpYiIsCiAgICAgICAgICAgICBzZWxlY3QudmFyID0gbGlzdChjb250cmliID0gMTApCiAgICAgICAgICAgICkgKwogIHNjYWxlX2NvbG9yX2dyYWRpZW50Mihsb3cgPSAid2hpdGUiLAogICAgICAgICAgICAgICAgICAgICAgICBtaWQgPSAiYmx1ZSIsIAogICAgICAgICAgICAgICAgICAgICAgICBoaWdoID0gInJlZCIsCiAgICAgICAgICAgICAgICAgICAgICAgIG1pZHBvaW50ID0gMy44CiAgICAgICAgICAgICAgICAgICAgICAgKQoKClBjYVJlc19SdGVzIDwtIGdldF9wY2FfaW5kKFBjYVJlcykKUGNhUmVzX1J0ZXMKIyBXaGVyZSByb3V0ZXMgbGllIGluIHJlbGF0aW9uIHRvIHRoZSBlaWdlbnZlY3RvcnMKUGNhUmVzX1J0ZXMkY29vcmQKCiMgR3JhcGggb2YgUm91dGUgTWFwCmZ2aXpfcGNhX2luZChQY2FSZXMsCiAgICAgICAgICAgICBjb2wuaW5kPSJjb3MyIgogICAgICAgICAgICApICsKICBzY2FsZV9jb2xvcl9ncmFkaWVudDIobG93ID0gIndoaXRlIiwKICAgICAgICAgICAgICAgICAgICAgICAgbWlkID0gImJsdWUiLAogICAgICAgICAgICAgICAgICAgICAgICBoaWdoID0gInJlZCIsCiAgICAgICAgICAgICAgICAgICAgICAgIG1pZHBvaW50ID0gMC41MAogICAgICAgICAgICAgICAgICAgICAgICkgKwogICMgY29tbWVudCBvdXQgeGxpbSBhbmQgeWxpbSB0byBzZWUgRVhUUkVNRSBvdXRsaWVyIFJvdXRlcwogIHhsaW0oLTUsIDUpICsKICB5bGltKC01LCA1KQoKIyBHcmFwaCBvZiBSb3V0ZSBNYXAgKHRvcCAxMCBjb250cmlidXRpbmcgdmFyaWFibGVzKQpmdml6X3BjYV9pbmQoUGNhUmVzLAogICAgICAgICAgICAgY29sLmluZD0iY29zMiIsCiAgICAgICAgICAgICBzZWxlY3QudmFyID0gbGlzdChjb3MyID0gMTApCiAgICAgICAgICAgICkgKwogIHNjYWxlX2NvbG9yX2dyYWRpZW50Mihsb3cgPSAid2hpdGUiLAogICAgICAgICAgICAgICAgICAgICAgICBtaWQgPSAiYmx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgIGhpZ2ggPSAicmVkIiwKICAgICAgICAgICAgICAgICAgICAgICAgbWlkcG9pbnQgPSAwLjUwCiAgICAgICAgICAgICAgICAgICAgICAgKSArCiAgIyBjb21tZW50IG91dCB4bGltIGFuZCB5bGltIHRvIHNlZSBFWFRSRU1FIG91dGxpZXIgUm91dGVzCiAgeGxpbSgtNSwgNSkgKwogIHlsaW0oLTUsIDUpCgojIEluc3BlY3Rpbmcgd2hhdCBsb29rcyB0byBiZSBhbiBFWFRSRU1FIG91dGxpZXIgcm91dGUKVmlldyhmaWx0ZXIoV2FpdFRpbWVfUnRlQ250cywKICAgICAgICAgICAgUm91dGUgPT0gIlNIOTkiCiAgICAgICAgICAgKQogICAgKQoKCiMgQmlwbG90IG9mIFJvdXRlcyBhbmQgVmFyaWFibGVzCmZ2aXpfcGNhX2JpcGxvdChQY2FSZXMsICBnZW9tID0gInRleHQiKSArCiAgeGxpbSgtNSwgNSkgKwogIHlsaW0oLTUsIDUpCgoKCiMgOSBlaWdlbnZhbHVlcyBnaXZlIH4gOTAlIG9mIHRoZSB2YXJpYW5jZQojICJlbGJvdyIgYXQgfjZ0aCBQcmluY2lwYWwgQ29tcG9uZW50CiMgfiA4IGVpZ2VudmFsdWVzID4gMSAoUEMgYWNjb3VudHMgZm9yIG1vcmUgdmFyaWFuY2UgdGhhbiBhY2NvdW50ZWQgdGhlIG9yaWdpbmFsIHN0YW5kYXJkaXplZCB2YXJpYWJsZXMpClZpZXcoZ2V0X2VpZ2VudmFsdWUoUGNhUmVzKSkKZnZpel9zY3JlZXBsb3QoUGNhUmVzLCBuY3AgPSAxNSkKZnZpel9zY3JlZXBsb3QoUGNhUmVzLCBuY3AgPSAxNSwgY2hvaWNlID0gImVpZ2VudmFsdWUiKQoKCiMgQ3JlYXRlIGEgZGF0YWZyYW1lIGZvciB0aGUgInRvcCIgOCBQQ3MKUm91dGVTdGF0c19QY2FfOEVpZ24gPC0gYXMuZGF0YS5mcmFtZShQY2FSZXNfUnRlcyRjb29yZCkgJT4lIAogIHNlbGVjdChEaW0uMSwKICAgICAgICAgRGltLjIsCiAgICAgICAgIERpbS4zLAogICAgICAgICBEaW0uNCwKICAgICAgICAgRGltLjUsCiAgICAgICAgIERpbS42LAogICAgICAgICBEaW0uNywKICAgICAgICAgRGltLjgKICAgICAgICApCgpWaWV3KFJvdXRlU3RhdHNfUGNhXzhFaWduKQoKYGBgCgoKQ2x1c3RlcmluZyAodXNpbmcgdGhlIFByaW5jaXBhbCBDb21wb25lbnRzIGNvbXB1dGVkIHVzaW5nIGNhcmV0OjpwcmVQcm9jZXNzKS4KCkFyZSB0aGUgZGF0YSBjbHVzdGVyYWJsZT8KYGBge3J9CgojIyMjIyBBcmUgdGhlIGRhdGEgY2x1c3RlcmFibGU/CiMgZ3JhZGllbnRfY29sIDwtIGxpc3QobG93ID0gInN0ZWVsYmx1ZSIsIGhpZ2ggPSAid2hpdGUiKQpDbHVzdERhdGFfRW5kcyA8LSBnZXRfY2x1c3RfdGVuZGVuY3koUm91dGVTdGF0c19QY2EsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuID0gbnJvdyhSb3V0ZVN0YXRzX1BjYQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApIC0gMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgZ3JhZGllbnQgPSBncmFkaWVudF9jb2wsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZWVkID0gMTIzNDU2Nzg5CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCnN0cihDbHVzdERhdGFfRW5kcykKCiMgSG9wa2lucyBzdGF0aXN0aWMKQ2x1c3REYXRhX0VuZHMkaG9wa2luc19zdGF0ICAjIHZhbHVlIG9mIDAuMTY1NzQ5NCBpbXBsaWVzIHRoYXQgdGhlIGRhdGEgYXJlIG5vdCB1bmlmb3JtbHkgZGlzdHJpYnV0ZWQgKHRoZXkgYXJlICJjbHVzdGVyYWJsZSIpCgojcGxvdApDbHVzdERhdGFfRW5kcyRwbG90CgpgYGAKCgpDbHVzdGVyaW5nLiBIb3cgbWFueSBjbHVzdGVycyBhcmUgdGhlcmU/CgprbWVhbnMsIHBhbSwgYW5kIGhpZXJhcmNoaWNhbCBjbHVzdHJpbmcgbWV0aG9kcywgdXNpbmcgd2l0aGluIHN1bSBvZiBzcXVhcmVzIGFuZCBzaWxob3VldHRlIG1lYXN1cmVzLgpgYGB7cn0KCiMgY2xhc3MoUm91dGVTdGF0c19QY2EpCgpmdml6X25iY2x1c3QoUm91dGVTdGF0c19QY2EsIGttZWFucywgbWV0aG9kID0gIndzcyIpICAjIH44IGNsdXN0ZXJzCmZ2aXpfbmJjbHVzdChSb3V0ZVN0YXRzX1BjYSwgcGFtLCBtZXRob2QgPSAid3NzIikgICMgfjYgY2x1c3RlcnMKZnZpel9uYmNsdXN0KFJvdXRlU3RhdHNfUGNhLCBoY3V0LCBtZXRob2QgPSAid3NzIikgICMgfjYgY2x1c3RlcnMKCmZ2aXpfbmJjbHVzdChSb3V0ZVN0YXRzX1BjYSwga21lYW5zLCBtZXRob2QgPSAic2lsaG91ZXR0ZSIpICAjIDIgY2x1c3RlcnMKZnZpel9uYmNsdXN0KFJvdXRlU3RhdHNfUGNhLCBwYW0sIG1ldGhvZCA9ICJzaWxob3VldHRlIikgICMgMiBjbHVzdGVycwpmdml6X25iY2x1c3QoUm91dGVTdGF0c19QY2EsIGhjdXQsIG1ldGhvZCA9ICJzaWxob3VldHRlIiwKICAgICAgICAgICAgIGhjX21ldGhvZCA9ICJjb21wbGV0ZSIpICAjIDIgY2x1c3RlcnMKCmBgYAoKCkNsdXN0ZXJpbmcuIEhvdyBtYW55IGNsdXN0ZXJzIGFyZSB0aGVyZT8KCmttZWFucyBtZXRob2Qgd2l0aCB0aGUgZ2FwIHN0YXRpc3RpYywgdXNpbmcgYm9vdHN0cmFwLgpgYGB7cn0KCiMgQ29tcHV0ZSBnYXAgc3RhdGlzdGljCiMga21lYW5zIHZlcnNpb24Kc2V0LnNlZWQoMTIzNDU2Nzg5KQojIHN5c3RlbS50aW1lKApnYXBfc3RhdF9rbSA8LSBjbHVzR2FwKFJvdXRlU3RhdHNfUGNhLAogICAgICAgICAgICAgICAgICAgICAgIEZVTiA9IGttZWFucywKICAgICAgICAgICAgICAgICAgICAgICBuc3RhcnQgPSAyNSwKICAgICAgICAgICAgICAgICAgICAgICBLLm1heCA9IDEwLAogICAgICAgICAgICAgICAgICAgICAgIEIgPSA1MDAKICAgICAgICAgICAgICAgICAgICAgICkKIyApCgojIFByaW50CnByaW50KGdhcF9zdGF0X2ttLCBtZXRob2QgPSAiVGliczIwMDFTRW1heCIpCnByaW50KGdhcF9zdGF0X2ttKQoKCiMgcGFtIHZlcnNpb24Kc2V0LnNlZWQoMTIzNDU2Nzg5KQpnYXBfc3RhdF9wbSA8LSBjbHVzR2FwKFJvdXRlU3RhdHNfUGNhLAogICAgICAgICAgICAgICAgICAgICAgIEZVTiA9IHBhbSwKICAgICAgICAgICAgICAgICAgICAgICBLLm1heCA9IDEwLAogICAgICAgICAgICAgICAgICAgICAgIEIgPSA1MDAKICAgICAgICAgICAgICAgICAgICAgICkKCiMgUHJpbnQKcHJpbnQoZ2FwX3N0YXRfcG0sIG1ldGhvZCA9ICJUaWJzMjAwMVNFbWF4IikKcHJpbnQoZ2FwX3N0YXRfcG0pCgoKIyBoaWVyYXJjaGljYWwgdmVyc2lvbgpzZXQuc2VlZCgxMjM0NTY3ODkpCmdhcF9zdGF0X2hjdXQgPC0gY2x1c0dhcChSb3V0ZVN0YXRzX1BjYSwKICAgICAgICAgICAgICAgICAgICAgICAgIEZVTiA9IGhjdXQsCiAgICAgICAgICAgICAgICAgICAgICAgICBLLm1heCA9IDEwLAogICAgICAgICAgICAgICAgICAgICAgICAgQiA9IDUwMAogICAgICAgICAgICAgICAgICAgICAgICApCgojIFByaW50CnByaW50KGdhcF9zdGF0X2hjdXQsIG1ldGhvZCA9ICJUaWJzMjAwMVNFbWF4IikKcHJpbnQoZ2FwX3N0YXRfaGN1dCkKCgoKIyBQbG90IGttZWFucwpmdml6X2dhcF9zdGF0KGdhcF9zdGF0X2ttLCAKICAgICAgICAgICAgICBtYXhTRSA9IGxpc3QobWV0aG9kID0gIlRpYnMyMDAxU0VtYXgiKQogICAgICAgICAgICAgKSAgIyAxIGNsdXN0ZXIKCiMgUGxvdCBwYW0KZnZpel9nYXBfc3RhdChnYXBfc3RhdF9wbSwgCiAgICAgICAgICAgICAgbWF4U0UgPSBsaXN0KG1ldGhvZCA9ICJUaWJzMjAwMVNFbWF4IikKICAgICAgICAgICAgICkgICMgMiBjbHVzdGVyCgojIFBsb3QgaGllcmFyY2hpY2FsCmZ2aXpfZ2FwX3N0YXQoZ2FwX3N0YXRfaGN1dCwgCiAgICAgICAgICAgICAgbWF4U0UgPSBsaXN0KG1ldGhvZCA9ICJUaWJzMjAwMVNFbWF4IikKICAgICAgICAgICAgICkgICMgMSBjbHVzdGVyCgpgYGAKCgpDbHVzdGVyaW5nLiBIb3cgbWFueSBjbHVzdGVycyBhcmUgdGhlcmU/CgprbWVhbnMgbWV0aG9kIHdpdGggdmFyaW91cyBkaWZmZXJlbnQgc3RhdGlzdGljcy4KYGBge3J9CgojIHN0cihpcmlzKQoKbmIgPC0gTmJDbHVzdChSb3V0ZVN0YXRzX1BjYSwgI3NjYWxlKGlyaXNbICwtNV0pLAogICAgICAgICAgICAgIGRpc3RhbmNlID0gImV1Y2xpZGVhbiIsCiAgICAgICAgICAgICAgbWluLm5jID0gMiwKICAgICAgICAgICAgICBtYXgubmMgPSAxNSwKICAgICAgICAgICAgICBtZXRob2QgPSAia21lYW5zIiwKICAgICAgICAgICAgICBpbmRleCA9ICJhbGwiCiAgICAgICAgICAgICApCgpmdml6X25iY2x1c3QobmIpCgpgYGAKCgpDbHVzdGVyaW5nLiBIb3cgbWFueSBjbHVzdGVycyBhcmUgdGhlcmU/CgpIaWVyYXJjaGljYWwgY2x1c3RlcmluZyBtZXRob2QuIFBhcnRpY3VsYXJseSBsb29raW5nIGF0IHNpbGhvdWV0dGUgc3RhdGlzdGljcy4KYGBge3J9CgojIEhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nLCBjdXQgaW4gMiB0byAxNSBncm91cHMKZm9yKGkgaW4gMjoxNSkgewogIGFzc2lnbihwYXN0ZTAoIkhDUmVzX0siLCBpKSwKICAgICAgICAgZWNsdXN0KFJvdXRlU3RhdHNfUGNhLAogICAgICAgICAgICAgICAgImhjbHVzdCIsCiAgICAgICAgICAgICAgICBrID0gaSwKICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJjb21wbGV0ZSIsCiAgICAgICAgICAgICAgICBncmFwaCA9IEZBTFNFCiAgICAgICAgICAgICAgICkKICAgICAgICApCiAgCiAgYXNzaWduKCJ4IiwKICAgICAgICAgZ2V0KHBhc3RlMCgiSENSZXNfSyIsIGkpCiAgICAgICAgICAgICkKICAgICAgICApCiAgCiAgYXNzaWduKHBhc3RlMCgiSENTdGF0c19LIiwgaSksCiAgICAgICAgIGNsdXN0ZXIuc3RhdHMoZGlzdChSb3V0ZVN0YXRzX1NjYWxlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ImV1Y2xpZGVhbiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgICAgICAgICB4JGNsdXN0ZXIKICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICApCiAgCiAgYXNzaWduKCJ5IiwKICAgICAgICAgZ2V0KHBhc3RlMCgiSENTdGF0c19LIiwgaSkKICAgICAgICAgICAgKQogICAgICAgICkKICAKICBhc3NpZ24ocGFzdGUwKCJIQ0RlbmRfSyIsIGkpLAogICAgICAgICBmdml6X2RlbmQoeCwgcmVjdCA9IFRSVUUsIHNob3dfbGFiZWxzID0gRkFMU0UpCiAgICAgICAgKQogIAogIGFzc2lnbihwYXN0ZTAoIkhDU2lsX0siLCBpKSwKICAgICAgICAgZnZpel9zaWxob3VldHRlKHgpCiAgICAgICAgKQogIAogIGFzc2lnbihwYXN0ZTAoIkhDU2lsV2lkdGhfSyIsIGkpLAogICAgICAgICBhcy5kYXRhLmZyYW1lKHkkY2x1cy5hdmcuc2lsd2lkdGhzKSAlPiUgCiAgICAgICAgICAgbXV0YXRlKEtWYWwgPSAxOm5yb3coLikKICAgICAgICAgICAgICAgICApCiAgICAgICAgKQogIH0KCgpIQ1NpbFdpZHRoX0FsbEsgPC0gbGVmdF9qb2luKHNlbGVjdChIQ1NpbFdpZHRoX0sxNSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgS1ZhbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYHkkY2x1cy5hdmcuc2lsd2lkdGhzYAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgSENTaWxXaWR0aF9LMTQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnkgPSBjKCJLVmFsIiA9ICJLVmFsIikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIGxlZnRfam9pbiguLAogICAgICAgICAgICBIQ1NpbFdpZHRoX0sxMywKICAgICAgICAgICAgYnkgPSBjKCJLVmFsIiA9ICJLVmFsIikKICAgICAgICAgICApICU+JSAKICBsZWZ0X2pvaW4oLiwKICAgICAgICAgICAgSENTaWxXaWR0aF9LMTIsCiAgICAgICAgICAgIGJ5ID0gYygiS1ZhbCIgPSAiS1ZhbCIpCiAgICAgICAgICAgKSAlPiUgCiAgbGVmdF9qb2luKC4sCiAgICAgICAgICAgIEhDU2lsV2lkdGhfSzExLAogICAgICAgICAgICBieSA9IGMoIktWYWwiID0gIktWYWwiKQogICAgICAgICAgICkgJT4lIAogIGxlZnRfam9pbiguLAogICAgICAgICAgICBIQ1NpbFdpZHRoX0sxMCwKICAgICAgICAgICAgYnkgPSBjKCJLVmFsIiA9ICJLVmFsIikKICAgICAgICAgICApICU+JSAKICBsZWZ0X2pvaW4oLiwKICAgICAgICAgICAgSENTaWxXaWR0aF9LOSwKICAgICAgICAgICAgYnkgPSBjKCJLVmFsIiA9ICJLVmFsIikKICAgICAgICAgICApICU+JSAKICBsZWZ0X2pvaW4oLiwKICAgICAgICAgICAgSENTaWxXaWR0aF9LOCwKICAgICAgICAgICAgYnkgPSBjKCJLVmFsIiA9ICJLVmFsIikKICAgICAgICAgICApICU+JSAKICBsZWZ0X2pvaW4oLiwKICAgICAgICAgICAgSENTaWxXaWR0aF9LNywKICAgICAgICAgICAgYnkgPSBjKCJLVmFsIiA9ICJLVmFsIikKICAgICAgICAgICApICU+JSAKICBsZWZ0X2pvaW4oLiwKICAgICAgICAgICAgSENTaWxXaWR0aF9LNiwKICAgICAgICAgICAgYnkgPSBjKCJLVmFsIiA9ICJLVmFsIikKICAgICAgICAgICApICU+JSAKICBsZWZ0X2pvaW4oLiwKICAgICAgICAgICAgSENTaWxXaWR0aF9LNSwKICAgICAgICAgICAgYnkgPSBjKCJLVmFsIiA9ICJLVmFsIikKICAgICAgICAgICApICU+JSAKICBsZWZ0X2pvaW4oLiwKICAgICAgICAgICAgSENTaWxXaWR0aF9LNCwKICAgICAgICAgICAgYnkgPSBjKCJLVmFsIiA9ICJLVmFsIikKICAgICAgICAgICApICU+JSAKICBsZWZ0X2pvaW4oLiwKICAgICAgICAgICAgSENTaWxXaWR0aF9LMywKICAgICAgICAgICAgYnkgPSBjKCJLVmFsIiA9ICJLVmFsIikKICAgICAgICAgICApICU+JSAKICBsZWZ0X2pvaW4oLiwKICAgICAgICAgICAgSENTaWxXaWR0aF9LMiwKICAgICAgICAgICAgYnkgPSBjKCJLVmFsIiA9ICJLVmFsIikKICAgICAgICAgICApCiAgCmNvbG5hbWVzKEhDU2lsV2lkdGhfQWxsSykgPC0gYygiS1ZhbCIsICJLMTUiLCAiSzE0IiwgIksxMyIsICJLMTIiLCAiSzExIiwgIksxMCIsICJLOSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSzgiLCAiSzciLCAiSzYiLCAiSzUiLCAiSzQiLCAiSzMiLCAiSzIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCgojIFZpc3VhbGl6ZQpIQ0RlbmRfSzIKSENEZW5kX0szCkhDRGVuZF9LNApIQ0RlbmRfSzUKSENEZW5kX0s2CkhDRGVuZF9LNwpIQ0RlbmRfSzgKSENEZW5kX0s5CkhDRGVuZF9LMTAKSENEZW5kX0sxMQpIQ0RlbmRfSzEyCkhDRGVuZF9LMTMKSENEZW5kX0sxNApIQ0RlbmRfSzE1CgpIQ1NpbF9LMgpIQ1NpbF9LMwpIQ1NpbF9LNApIQ1NpbF9LNQpIQ1NpbF9LNgpIQ1NpbF9LNwpIQ1NpbF9LOApIQ1NpbF9LOQpIQ1NpbF9LMTAKSENTaWxfSzExCkhDU2lsX0sxMgpIQ1NpbF9LMTMKSENTaWxfSzE0CkhDU2lsX0sxNQoKCkhDU2lsV2lkdGhfQWxsSwoKYGBgCgoKVXNpbmcga21lYW5zLCBQQU0sIGFuZCBIaWVyYXJjaGljYWwgY2x1c3RlcmluZyBtZXRob2RzLCB3ZSBjYW4gc2F5IHdlIHByb2JhYmx5IGhhdmUgYXJvdW4gMiBjbHVzdGVycy4KCkxldCdzIHRyeSBkZW5zaXR5IGNsdXN0ZXJpbmcuICAoVGhpcyB0ZW5kcyB0byBzaG93IHRoYXQgbWF5YmUgdGhlcmUgaXMgb25seSBvbmUgImNsdXN0ZXIsIiBtZWFuaW5nIHRoYXQgZGF0YSBhcmUgbm90IGNsdXN0ZXJhYmxlLikKYGBge3J9CgpybShsaXN0ID0gbHMocGF0dGVybiA9ICJfSyIpCiAgKQoKCiMgQ29tcHV0ZSBEQlNDQU4gdXNpbmcgZnBjIHBhY2thZ2UKa05OZGlzdHBsb3QoUm91dGVTdGF0c19QY2EsIGsgPSAxMCkKYWJsaW5lKGggPSA4LjUsIGx0eSA9IDIpCgpzZXQuc2VlZCgxMjM0NTY3ODkpCmRiIDwtIGZwYzo6ZGJzY2FuKFJvdXRlU3RhdHNfUGNhLAogICAgICAgICAgICAgICAgICBlcHMgPSA4LjUsCiAgICAgICAgICAgICAgICAgIE1pblB0cyA9IDEwCiAgICAgICAgICAgICAgICApCgpzdHIoZGIpCmRiCgojIFBsb3QgREJTQ0FOIHJlc3VsdHMKZnZpel9jbHVzdGVyKGRiLAogICAgICAgICAgICAgUm91dGVTdGF0c19QY2EsCiAgICAgICAgICAgICBzdGFuZCA9IEZBTFNFLAogICAgICAgICAgICAgZnJhbWUgPSBGQUxTRSwKICAgICAgICAgICAgIGdlb20gPSAicG9pbnQiCiAgICAgICAgICAgICkKCmBgYAoKCldlIGNhbiBzYXkgdGhhdCBNQVlCRSB0aGVyZSBhcmUgdHdvIGNsdXN0ZXJzLCBidXQgdGhlcmUgaXMgbW9yZSBldmlkZW5jZSBmb3IgcHJvYmFibHkganVzdCBvbmUgY2x1c3RlciAoaS5lLiwgdGhlIGRhdGEgYXJlIE5PVCBjbHVzdGVyYWJsZSkuCmBgYHtyfQoKIyByZW1vdmUgbm8gbG9uZ2VyIG5lZWRlZCBpdGVtcwpybShYMl9Mb25nLCBYMl9QY3QsIENsdXN0RGF0YV9FbmRzLCBkYiwgZ2FwX3N0YXQsIGdhcF9zdGF0X2hjdXQsIGdhcF9zdGF0X2ttLCBnYXBfc3RhdF9wbSwgaSwgbmIsIHJkLCBUcm5zZnJtLCB4LCB5KQoKYGBgCgoKCgoKCgpJbnZlc3RpZ2F0aW5nIFRyYXZlbFRpbWVfU2VjLgpgYGB7cn0KClZpZXcoZmlsdGVyKFRUTGFyZ2VSdGVDaG5nLAogICAgICAgICAgICAhaXMubmEoVHJhdmVsVGltZV9TZWMpICYKICAgICAgICAgICAgICBSdGVDaGFuZ2UyID09ICJTYW1lIgogICAgICAgICAgICkgJT4lIAogICAgICAgYXJyYW5nZShkZXNjKFRyYXZlbFRpbWVfU2VjKSwKICAgICAgICAgICAgICAgU3BlZWRBdmdfTXBoX05ld0h2cnMKICAgICAgICAgICAgICApICU+JQogICAgICAgaGVhZCg1MDApCiAgICApCgoKIyBleGFtcGxlcyB3aGVyZSBUcmF2ZWxUaW1lX1NlYyBpcyBzbWFsbCAoMSBzZWMpIGFuZCBTcGVlZEF2Z19NcGhfTmV3SHZycyBpcyBsYXJnZS4KVmlldyhzZWxlY3QoTmV3VHJhdlRpbWUsCiAgICAgICAgICAgICMgLW1hdGNoZXMoIihxKDJ8NXwoOTUpfCg5OCkpKXxNZWFufE1lZHxDbnQiKQogICAgICAgICAgICAtKFREX01pX3EyOlREX01pX1NTSEdfQ250X0YpLAogICAgICAgICAgICAtKFRUX0hyX3EyOlRUX0hyX1NTSEdfQ250X0YpCiAgICAgICAgICAgKSAlPiUgCiAgICAgICBmaWx0ZXIoKFJvd051bV9PRyA+PSAyMjE3MzUzICYgUm93TnVtX09HIDw9IDIyMTczNzMpIHwgIyAyMjE3MzYzCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDMwOTAzMjEgJiBSb3dOdW1fT0cgPD0gMzA5MDM0MSkgfCAjIDMwOTAzMzEKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gODA3NjQgJiBSb3dOdW1fT0cgPD0gODA3ODQpIHwgIyA4MDc3NAogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAzMzg0MCAmIFJvd051bV9PRyA8PSAzMzg2MCkgIyAzMzg1MAogICAgICAgICAgICkKICAgICkKCgoKCgoKIyBleGFtcGxlcyB3aGVyZSBUcmF2ZWxUaW1lX1NlYyBpcyBsYXJnZSBhbmQgU3BlZWRBdmdfTXBoX05ld0h2cnMgaXMgc21hbGwuClZpZXcoZmlsdGVyKFRUTGFyZ2VSdGVDaG5nLAogICAgICAgICAgICAoUm93TnVtX09HID49IDIyNTAyOTAgJiBSb3dOdW1fT0cgPD0gMjI1MDMxMCkgfCAjIDIyNTAzMDAKICAgICAgICAgICAgICAoUm93TnVtX09HID49IDg2NzcxNyAmIFJvd051bV9PRyA8PSA4Njc3MzcpIHwgIyA4Njc3MjcKICAgICAgICAgICAgICAoUm93TnVtX09HID49IDg2NDM3OSAmIFJvd051bV9PRyA8PSA4NjQzOTkpIHwgIyA4NjQzODkKICAgICAgICAgICAgICAoUm93TnVtX09HID49IDgwODM5NSAmIFJvd051bV9PRyA8PSA4MDg0MTUpICMgODA4NDA1CiAgICAgICAgICAgKQogICAgKQpgYGAKCgoKCmBgYHtyfQoKICAgICAgICAgCiAgICAgICAgIAojIGV4YW1wbGVzIHdoZXJlIFRyYXZlbFRpbWVfU2VjIGlzIHVudXN1YWxseSBzbWFsbCAod2l0aCBUcmF2ZWxEaXN0YW5jZV9NaSB2YWx1ZXMgdGhhdCBhcmUgbGFyZ2UpLgpWaWV3KGZpbHRlcihBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gMTA0MjIyOCAmIFJvd051bV9PRyA8PSAxMDQyMjQ4KSB8ICMgMTA0MjIzOAogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSA1MzgxNiAmIFJvd051bV9PRyA8PSA1MzgzNikgfCAjIDUzODI2CiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDM2MDU3MSAmIFJvd051bV9PRyA8PSAzNjA1OTEpIHwgIyAzNjA1ODEKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gNTAyMjcxICYgUm93TnVtX09HIDw9IDUwMjI5MSkgIyA1MDIyODEgKGNhbid0IGV4cGxpYW4gdGhlIHdlaXJkIFRyYXZlbFRpbWVfU2VjIGNhbGN1bGF0aW9uIGhlcmUgLSBpdCdzIG5vdCBldmVuIGFuIGludGVnZXIhKQogICAgICAgICAgICkKICAgICkKCiMgc3RpbGwgdHJ5aW5nIHRvIGV4cGxhaW4gNTAyMjgxLi4ub24gdGhlIGRheSBvZiB0aGlzIHdlaXJkbmVzcywgdGhlIGJ1cyB3YXMgb25seSBpbiBjaXJjdWxhdGlvbiBmb3IgNC01IHN0b3BzICh+MjAgbWludXRlcykgb24gdGhhdCBkYXkgKE9jdCA2KQpWaWV3KGZpbHRlcihBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgIEJ1c19JRCA9PSAyNzExCiAgICAgICAgICAgKQogICAgKQoKCiMgZXhwbG9yaW5nIGxhcmdlIHZhbHVlcyBmb3IgVHJhdmVsVGltZV9TZWMKVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICBUcmF2ZWxUaW1lX1NlYyA9PSAzMDAKICAgICAgICAgICApICU+JSAKICAgICAgIGFycmFuZ2UoZGVzYyhUcmF2ZWxUaW1lX1NlYyksCiAgICAgICAgICAgICAgIFNwZWVkQXZnX01waDIKICAgICAgICAgICAgICApCiAgICApCgojIGV4YW1wbGVzIHdoZXJlIFRyYXZlbFRpbWVfU2VjIGlzIHVudXN1YWxseSBsYXJnZSAod2l0aCBUcmF2ZWxEaXN0YW5jZV9NaSB2YWx1ZXMgdGhhdCBhcmUgc21hbGwsIHNvIFNwZWVkQXZnX01waCB2YWx1ZXMgYXJlIHZlcnkgc21hbGwpLgpWaWV3KGZpbHRlcihBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gMjYyNzQ1OSAmIFJvd051bV9PRyA8PSAyNjI3NDc5KSB8ICMgMjYyNzQ2OQogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAyMTkzMzQ0ICYgUm93TnVtX09HIDw9IDIxOTMzNjQpIHwgIyAyMTkzMzU0CiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDE2NDQxMjMgJiBSb3dOdW1fT0cgPD0gMTY0NDE0MykgfCAjIDE2NDQxMzMKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gODY5NjAwICYgUm93TnVtX09HIDw9IDg2OTYyMCkgIyA4Njk2MTAKICAgICAgICAgICApCiAgICApCgpgYGAKCkludmVzdGlnYXRpb24gb2YgU3BlZWRBdmdfTXBoMgoKVmlldyhTcGVlZF9QY3RpbGVzKTogOTAlIG9mIFNwZWVkQXZnX01waDIgYXJlIGJldHdlZW4gfjNtcGggYW5kIH42Nm1waC4KYGBge3J9CgpTcGVlZF9OdGlsZSA8LSBhcy5kYXRhLmZyYW1lKEFsbERheXNfTmV3VHJhdmVsRGlzdCRTcGVlZEF2Z19NcGgyKSAlPiUgCiAgbXV0YXRlKFBjdGlsZSA9IG50aWxlKEFsbERheXNfTmV3VHJhdmVsRGlzdCRTcGVlZEF2Z19NcGgyLCAxMDApLAogICAgICAgICBNaW5SID0gbWluX3JhbmsoQWxsRGF5c19OZXdUcmF2ZWxEaXN0JFNwZWVkQXZnX01waDIpLAogICAgICAgICBQY3RSID0gcGVyY2VudF9yYW5rKEFsbERheXNfTmV3VHJhdmVsRGlzdCRTcGVlZEF2Z19NcGgyKSwKICAgICAgICAgUGN0Ul9Sb3VuZCA9IHJvdW5kKFBjdFIsIDIpCiAgICAgICAgKSAKCmNvbG5hbWVzKFNwZWVkX050aWxlKVsxXSA8LSAiU3BlZWRBdmdfTXBoMiIKc3RyKFNwZWVkX050aWxlKQoKU3BlZWRfTnRpbGVfUm93cyA8LSBucm93KFNwZWVkX050aWxlKQoKVmlldyh0YWlsKFNwZWVkX050aWxlLCA1MDApKQoKClNwZWVkX1BjdGlsZXMgPC0gZ3JvdXBfYnkoU3BlZWRfTnRpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgUGN0Ul9Sb3VuZAogICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgc3VtbWFyaXNlKAogICAgTWluU3BlZWRBdFBjdGlsZSA9IG1pbihTcGVlZEF2Z19NcGgyKSwKICAgIENudHNBdFBjdGlsZSA9IG4oKSwKICAgIFBjdHNBdFBjdGlsZSA9IENudHNBdFBjdGlsZSAvIFNwZWVkX050aWxlX1Jvd3MKICApICU+JSAKICBtdXRhdGUoQ3VtU3VtUEF0UCA9IGN1bXN1bShQY3RzQXRQY3RpbGUpCiAgICAgICAgKQoKVmlldyhTcGVlZF9QY3RpbGVzKQoKYGBgCgpJbnZlc3RpZ2F0aW9uIG9mIFNwZWVkQXZnX01waDIuCgpFeHBsb3JpbmcgdGhlIHJlbW92YWwgb2Ygb3V0bGllciBUcmF2ZWxUaW1lX1NlYyBhbmQgVHJhdmVsRGlzdGFuY2VfTWkuCmBgYHtyfQoKc3VtbWFyeShzZWxlY3QoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAgICBTcGVlZEF2Z19NcGgsCiAgICAgICAgICAgICAgIFNwZWVkQXZnX01waDIKICAgICAgICAgICAgICApCiAgICAgICApCgpzdW1tYXJ5KHNlbGVjdChmaWx0ZXIoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWkgPiAwLjAwMDE4OTM5MzkgJiAjIGxvd2VzdCBub24temVybyBwZXJjZW50aWxlCiAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbERpc3RhbmNlX01pIDwgMS4wODEyNTAwMDAwICYgIyA5OXRoIHBlcmNlbnRpbGUKICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsVGltZV9TZWMgPiAxMC4wNTAwMDAgJiAjIDJuZCBwZXJjZW50aWxlCiAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjIDwgMjkzLjAwMDAwMCAjIDk4dGggcGVyY2VudGlsZQogICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICBTcGVlZEF2Z19NcGgsCiAgICAgICAgICAgICAgIFNwZWVkQXZnX01waDIKICAgICAgICAgICAgICApCiAgICAgICApCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFNwZWVkQXZnX01waDIuCgpIaXN0b2dyYW0gb2YgU3BlZWRBdmdfTXBoMi4KYGBge3J9CgpTcGVlZF9IaXN0RGVuIDwtIGdncGxvdChmaWx0ZXIoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIWlzLm5hKFNwZWVkQXZnX01waDIpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gU3BlZWRBdmdfTXBoMiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSAuLmRlbnNpdHkuLgogICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSA1LCBmaWxsID0gImxpZ2h0Ymx1ZSIsIGNvbG91ciA9ICJncmV5NjAiLCBzaXplID0gMC4yKSArCiAgZ2VvbV9saW5lKHN0YXQgPSAiZGVuc2l0eSIsIGNvbG91ciA9ICJyZWQiKSArCiAgc3RhdF9iaW4oYmlud2lkdGggPSA1LAogICAgICAgICAgIGdlb20gPSAidGV4dCIsCiAgICAgICAgICAgc2l6ZSA9IDIuNSwKICAgICAgICAgICB2anVzdCA9IDEuNSwKICAgICAgICAgICBhZXMobGFiZWwgPSBmb3JtYXQoLi5jb3VudC4uLCBiaWcubWFyayA9ICIsIikKICAgICAgICAgICAgICApLAogICAgICAgICAgKSArCiAgIyBnZW9tX3RleHQoYWVzKGxhYmVsID0gZm9ybWF0KC4uY291bnQuLiwgYmlnLm1hcmsgPSAiLCIpCiAgIyAgICAgICAgICAgICAgKSwKICAjICAgICAgICAgICBzaXplID0gMywKICAjICAgICAgICAgICBudWRnZV95ID0gKC4uY291bnQuLiAqIDAuMSkKICAjICAgICAgICAgICkgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygwLCA3MCksIHlsaW0gPSBjKDAsIDAuMDQpCiAgICAgICAgICAgICAgICAgKSArCiAgIyAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKwogIGxhYnModGl0bGUgPSAiVmFyaWF0aW9uIGluIFRyYXZlbCBTcGVlZCIsCiAgICAgICB4ID0gIkF2ZXJhZ2UgU3BlZWQgKG1waCkiLAogICAgICAgeSA9ICJEZW5zaXR5IgogICAgICApCgpTcGVlZF9IaXN0RGVuCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFNwZWVkQXZnX01waDIuCgpIaXN0b2dyYW0gb2YgU3BlZWRBdmdfTXBoMiBhZnRlciByZW1vdmluZyBvdXRsaWVyIFRyYXZlbFRpbWVfU2VjIGFuZCBUcmF2ZWxEaXN0YW5jZV9NaS4KYGBge3J9CgpWaWV3KFRyYXZEaXN0TWlOZXdfUGN0aWxlcykKVmlldyhUcmF2VGltZUhyX1BjdGlsZXMpCgpTcGVlZE5vT3V0bGllcl9IaXN0RGVuIDwtIGdncGxvdChmaWx0ZXIoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIWlzLm5hKFNwZWVkQXZnX01waDIpICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfTmV3ID4gMC4wNzc4NDEwMDUgJiAjIDV0aCBwZXJjZW50aWxlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgVHJhdmVsRGlzdGFuY2VfTWlfTmV3IDwgMS4wODEyNTAwMDAwICYgIyA5OXRoIHBlcmNlbnRpbGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsVGltZV9TZWMgPiAxMi4xMDAwMDAgIyA0dGggcGVyY2VudGlsZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIFRyYXZlbFRpbWVfU2VjIDwgMjkzLjAwMDAwMCAjIDk4dGggcGVyY2VudGlsZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IFNwZWVkQXZnX01waDIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gLi5kZW5zaXR5Li4KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgKwogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gNSwgZmlsbCA9ICJsaWdodGJsdWUiLCBjb2xvdXIgPSAiZ3JleTYwIiwgc2l6ZSA9IDAuMikgKwogIGdlb21fbGluZShzdGF0ID0gImRlbnNpdHkiLCBjb2xvdXIgPSAicmVkIikgKwogIHN0YXRfYmluKGJpbndpZHRoID0gNSwKICAgICAgICAgICBnZW9tID0gInRleHQiLAogICAgICAgICAgIHNpemUgPSAyLjUsCiAgICAgICAgICAgdmp1c3QgPSAxLjUsCiAgICAgICAgICAgYWVzKGxhYmVsID0gZm9ybWF0KC4uY291bnQuLiwgYmlnLm1hcmsgPSAiLCIpCiAgICAgICAgICAgICAgKSwKICAgICAgICAgICkgKwogICMgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IGZvcm1hdCguLmNvdW50Li4sIGJpZy5tYXJrID0gIiwiKQogICMgICAgICAgICAgICAgICksCiAgIyAgICAgICAgICAgc2l6ZSA9IDMsCiAgIyAgICAgICAgICAgbnVkZ2VfeSA9ICguLmNvdW50Li4gKiAwLjEpCiAgIyAgICAgICAgICApICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMCwgNzApLCB5bGltID0gYygwLCAwLjA0KQogICAgICAgICAgICAgICAgICkgKwogICMgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsKICBsYWJzKHRpdGxlID0gIlZhcmlhdGlvbiBpbiBUcmF2ZWwgU3BlZWQiLAogICAgICAgc3VidGl0bGUgPSAiKHJlbW92ZWQgbG93IG91dGxpZXJzIG9mIFRyYXZlbCBEaXN0YW5jZSBhbmQgVHJhdmVsIFRpbWUpIiwKICAgICAgIHggPSAiQXZlcmFnZSBTcGVlZCAobXBoKSIsCiAgICAgICB5ID0gIkRlbnNpdHkiCiAgICAgICkKClNwZWVkTm9PdXRsaWVyX0hpc3REZW4KCmBgYAoKCkludmVzdGlnYXRpb24gb2YgU3BlZWRBdmdfTXBoMi4KCk5ldyBkYXRhc2V0IChOb091dGxpZXJzX1RyYXZlbERpc3ROVGltZSkgd2hlbiByZW1vdmluZyBvdXRsaWVyIGxvdyB2YWx1ZXMgb2YgVHJhdmVsRGlzdGFuY2VfTWlfTmV3IGFuZCBUcmF2ZWxUaW1lX1NlYy4KYGBge3J9CgpWaWV3KFRyYXZEaXN0TWlOZXdfUGN0aWxlcykKVmlldyhUcmF2VGltZUhyX1BjdGlsZXMpCgpOb091dGxpZXJzX1RyYXZlbERpc3ROVGltZSA8LSBmaWx0ZXIoQWxsRGF5c19OZXdUcmF2ZWxEaXN0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsRGlzdGFuY2VfTWlfTmV3ID4gLjA3Nzg0MTAwNSAmICMgNXRoIHBlcmNlbnRpbGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBUcmF2ZWxEaXN0YW5jZV9NaV9OZXcgPCAxLjA4MTI1MDAwMDAgJiAjIDk5dGggcGVyY2VudGlsZQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUcmF2ZWxUaW1lX1NlYyA+IDEyLjEwMDAwMCAjIDR0aCBwZXJjZW50aWxlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgVHJhdmVsVGltZV9TZWMgPCAyOTMuMDAwMDAwICMgOTh0aCBwZXJjZW50aWxlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKCm5yb3coQWxsRGF5c19OZXdUcmF2ZWxEaXN0KSAtIG5yb3coTm9PdXRsaWVyc19UcmF2ZWxEaXN0TlRpbWUpCgpzdHIoTm9PdXRsaWVyc19UcmF2ZWxEaXN0TlRpbWUpCnN1bW1hcnkoTm9PdXRsaWVyc19UcmF2ZWxEaXN0TlRpbWUpCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFNwcGVkQXZnX01waDIuCgpWaWV3KFNwZWVkX05vT3V0X1BjdGlsZXMpOiAgQXByb3hpbWF0ZWx5IDkwJSBvZiBTcGVlZEF2Z19NcGgyIHZhbHVlcyBhcmUgYmV0d2VlbiB+NG1waCBhbmQgfjU2bXBoLgpgYGB7cn0KClNwZWVkX05vT3V0X050aWxlIDwtIGFzLmRhdGEuZnJhbWUoTm9PdXRsaWVyc19UcmF2ZWxEaXN0TlRpbWUkU3BlZWRBdmdfTXBoMikgJT4lIAogIG11dGF0ZShQY3RpbGUgPSBudGlsZShOb091dGxpZXJzX1RyYXZlbERpc3ROVGltZSRTcGVlZEF2Z19NcGgyLCAxMDApLAogICAgICAgICBNaW5SID0gbWluX3JhbmsoTm9PdXRsaWVyc19UcmF2ZWxEaXN0TlRpbWUkU3BlZWRBdmdfTXBoMiksCiAgICAgICAgIFBjdFIgPSBwZXJjZW50X3JhbmsoTm9PdXRsaWVyc19UcmF2ZWxEaXN0TlRpbWUkU3BlZWRBdmdfTXBoMiksCiAgICAgICAgIFBjdFJfUm91bmQgPSByb3VuZChQY3RSLCAyKQogICAgICAgICkgCgpjb2xuYW1lcyhTcGVlZF9Ob091dF9OdGlsZSlbMV0gPC0gIlNwZWVkQXZnX01waDIiCnN0cihTcGVlZF9Ob091dF9OdGlsZSkKClNwZWVkX05vT3V0X050aWxlX1Jvd3MgPC0gbnJvdyhTcGVlZF9Ob091dF9OdGlsZSkKClZpZXcodGFpbChTcGVlZF9Ob091dF9OdGlsZSwgNTAwKSkKCgpTcGVlZF9Ob091dF9QY3RpbGVzIDwtIGdyb3VwX2J5KFNwZWVkX05vT3V0X050aWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBjdFJfUm91bmQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHN1bW1hcmlzZSgKICAgIE1pblNwZWVkQXRQY3RpbGUgPSBtaW4oU3BlZWRBdmdfTXBoMiksCiAgICBDbnRzQXRQY3RpbGUgPSBuKCksCiAgICBQY3RzQXRQY3RpbGUgPSBDbnRzQXRQY3RpbGUgLyBTcGVlZF9Ob091dF9OdGlsZV9Sb3dzCiAgKSAlPiUgCiAgbXV0YXRlKEN1bVN1bVBBdFAgPSBjdW1zdW0oUGN0c0F0UGN0aWxlKQogICAgICAgICkKClZpZXcoU3BlZWRfTm9PdXRfUGN0aWxlcykKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgU3BwZWRBdmdfTXBoMi4KCkV4bG9yaW5nIG9kZC9pbXBvc3NpYmxlIHZhbHVlcy4KYGBge3J9CgojIEV4cGxvcmluZyB3aGVuIFNwZWVkQXZnX01waDIgaXMgTkEgIC0tICBkb2VzIG5vdCBvY2N1ciBhdCBhbGwKbnJvdyhmaWx0ZXIoTm9PdXRsaWVyc19UcmF2ZWxEaXN0TlRpbWUsCiAgICAgICAgICAgIGlzLm5hKFNwZWVkQXZnX01waDIpCiAgICAgICAgICAgKQogICAgKQoKCiMgRXhwbG9yaW5nIHdoZW4gU3BlZWRBdmdfTXBoMiBpcyB6ZXJvICAtLSAgZG9lcyBub3Qgb2NjdXIgYXQgYWxsCm5yb3coZmlsdGVyKE5vT3V0bGllcnNfVHJhdmVsRGlzdE5UaW1lLAogICAgICAgICAgICBTcGVlZEF2Z19NcGgyID09IDAKICAgICAgICAgICApCiAgICApCgoKIyBleGFtcGxlcyB3aGVyZSBTcGVlZEF2Z19NcGgyIDwgMy4yODQ4NzcwClZpZXcoZmlsdGVyKEFsbERheXNfTmV3VHJhdmVsRGlzdCwKICAgICAgICAgICAgU3BlZWRBdmdfTXBoMiA+IDAgJgogICAgICAgICAgICAgIFNwZWVkQXZnX01waDIgPCAzLjI4NDg3NzAKICAgICAgICAgICApICU+JSAKICAgICAgIGFycmFuZ2UoU3BlZWRBdmdfTXBoMikKICAgICkKCiMgZXhhbXBsZXMgd2hlcmUgU3BlZWRBdmdfTXBoMiA8IDMuMjg0ODc3MApWaWV3KGZpbHRlcihBbGxEYXlzX05ld1RyYXZlbERpc3QsCiAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gNDg1MzM4ICYgUm93TnVtX09HIDw9IDQ4NTM1OCkgfCAjIDQ4NTM0OCAgLS0gIEV4dHJlbWUgdHJhdmVsIHRpbWUsIFJvdXRlIENoYW5nZQogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAzNDY5NTIgJiBSb3dOdW1fT0cgPD0gMzQ2OTcyKSB8ICMgMzQ2OTYyICAtLSBFeHRyZW1lIHRyYXZlbCB0aW1lLCBSb3V0ZSBDaGFuZ2UgCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDcwNDk0ICYgUm93TnVtX09HIDw9IDcwNTE0KSB8ICMgNzA1MDQgIC0tICBFeHRyZW1lIHRyYXZlbCB0aW1lLCBSb3V0ZSBDaGFuZ2UKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gMjA1MTg0NiAmIFJvd051bV9PRyA8PSAyMDUxODY2KSAjIDIwNTE4NTYgIC0tICBFeHRyZW1lIHRyYXZlbCB0aW1lLCBSb3V0ZSBDaGFuZ2UKICAgICAgICAgICApCiAgICApCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIFNwZWVkQXZnX01waDIuCgpMaW1pdCB0aGUgZGF0YXNldCBiYXNlZCBvbiBTcGVlZEF2Z19NcGgyLgpgYGB7cn0KCk5vT3V0bGllcnNTcGVlZCA8LSBmaWx0ZXIoTm9PdXRsaWVyc19UcmF2ZWxEaXN0TlRpbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgYmV0d2VlbihTcGVlZEF2Z19NcGgyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgNC4wNjkzMDAsICMgNXRoIHBlcmNlbnRpbGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDU2LjA1NjUxICM5NXRoIHBlcmNlbnRpbGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICkKCm5yb3coTm9PdXRsaWVyc19UcmF2ZWxEaXN0TlRpbWUpIC0gbnJvdyhOb091dGxpZXJzU3BlZWQpCgpzdW1tYXJ5KE5vT3V0bGllcnNTcGVlZCkKCmBgYAoKClRyYXZlbFRpbWUgbm93IGxvb2tzIGxpa2UgaXQgaGFzIHNvbWUgb2RkIHZhbHVlcyBvbiB0aGUgaGlnaCBlbmQuICBTbyBsZXQncyBsb29rIGF0IHRob3NlLgoKVmlldyhUcmF2VGltZV9Ob091dF9QY3RpbGVzKTogIFZpcnR1YWxseSBhbGwgdHJpcHMgc2hvdWxkIHRha2UgbGVzcyB0aGFuIDUgbWludXRlcy4gKFRoZSA5OXRoIHBlcmNlbnRpbGUgb2Ygb2YgVHJhdmVsVGltZSBpcyBhcHByb3hpbWF0ZWx5IDggbWludXRlcy4pCmBgYHtyfQoKVHJhdlRpbWVfTm9PdXRfTnRpbGUgPC0gYXMuZGF0YS5mcmFtZShOb091dGxpZXJzU3BlZWQkVHJhdmVsVGltZV9IcikgJT4lIAogIG11dGF0ZShQY3RpbGUgPSBudGlsZShOb091dGxpZXJzU3BlZWQkVHJhdmVsVGltZV9IciwgMTAwKSwKICAgICAgICAgTWluUiA9IG1pbl9yYW5rKE5vT3V0bGllcnNTcGVlZCRUcmF2ZWxUaW1lX0hyKSwKICAgICAgICAgUGN0UiA9IHBlcmNlbnRfcmFuayhOb091dGxpZXJzU3BlZWQkVHJhdmVsVGltZV9IciksCiAgICAgICAgIFBjdFJfUm91bmQgPSByb3VuZChQY3RSLCAyKQogICAgICAgICkKCmNvbG5hbWVzKFRyYXZUaW1lX05vT3V0X050aWxlKVsxXSA8LSAiVHJhdmVsVGltZV9IciIKc3RyKFRyYXZUaW1lX05vT3V0X050aWxlKQoKVHJhdlRpbWVfTm9PdXRfTnRpbGVfUm93cyA8LSBucm93KFRyYXZUaW1lX05vT3V0X050aWxlKQoKVmlldyh0YWlsKFRyYXZUaW1lX05vT3V0X050aWxlLCA1MDApKQoKClRyYXZUaW1lX05vT3V0X1BjdGlsZXMgPC0gZ3JvdXBfYnkoVHJhdlRpbWVfTm9PdXRfTnRpbGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUGN0Ul9Sb3VuZAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgc3VtbWFyaXNlKAogICAgTWluVHJhdlRpbWVIckF0UGN0aWxlID0gbWluKFRyYXZlbFRpbWVfSHIpLAogICAgQ250c0F0UGN0aWxlID0gbigpLAogICAgUGN0c0F0UGN0aWxlID0gQ250c0F0UGN0aWxlIC8gVHJhdlRpbWVfTm9PdXRfTnRpbGVfUm93cwogICkgJT4lIAogIG11dGF0ZShDdW1TdW1QQXRQID0gY3Vtc3VtKFBjdHNBdFBjdGlsZSksCiAgICAgICAgIE1pblRyYXZUaW1lU2VjQXRQY3RpbGUgPSBNaW5UcmF2VGltZUhyQXRQY3RpbGUgKiAoNjAgKiA2MCkKICAgICAgICApCgpWaWV3KFRyYXZUaW1lX05vT3V0X1BjdGlsZXMpCgpgYGAKCgpJbnZlc3RpZ2F0aW5nIG9kZCBUcmF2ZWxUaW1lX1NlYyB2YWx1ZXMuCgpUcmlwcyBsb25nZXIgdGhhbiB+OCBtaW51dGVzLgpgYGB7cn0KClZpZXcoZmlsdGVyKE5vT3V0bGllcnNTcGVlZCwKICAgICAgICAgICAgVHJhdmVsVGltZV9TZWMgPiA0OTEgIyBtaW4gYXQgdGhlIDEwMHRoIHBlcmNlbnRpbGUKICAgICAgICAgICApICU+JSAKICAgICAgIGFycmFuZ2UoZGVzYyhUcmF2ZWxUaW1lX1NlYykKICAgICAgICAgICAgICApCiAgICApCgojIGV4YW1wbGVzIG9mIFRyYXZlbFRpbWVfU2VjIHZhbHVlcyB0aGF0IGFyZSBsYXJnZXN0LgpWaWV3KGZpbHRlcihOb091dGxpZXJzU3BlZWQsCiAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gMjA3MTc1OSAmIFJvd051bV9PRyA8PSAyMDcxNzc5KSB8ICMgMjA3MTc2OSAgLS0gIHJlc3VsdHMgZnJvbSBhIHJvdXRlIGNoYW5nZSwgYW5kIGEgM2hyKyB3YWl0IGJlZm9yZSB0aGUgbmV3IHJvdXRlIHN0YXJ0cwogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAxNDczNjg2ICYgUm93TnVtX09HIDw9IDE0NzM3MDYpIHwgIyAxNDczNjk2ICAtLSAgcmVzdWx0cyBmcm9tIGEgcm91dGUgY2hhbmdlLCBhbmQgYSAzaHIgd2FpdCBiZWZvcmUgdGhlIG5ldyByb3V0ZSBzdGFydHMKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gMTIyMjgyMiAmIFJvd051bV9PRyA8PSAxMjIyODQyKSB8ICMgMTIyMjgzMiAgLS0gIHJlc3VsdHMgZnJvbSBhIHJvdXRlIGNoYW5nZSwgYW5kIGEgM2hyIHdhaXQgYmVmb3JlIHRoZSBuZXcgcm91dGUgc3RhcnRzCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDMwNDYwODkgJiBSb3dOdW1fT0cgPD0gMzA0NjEwOSkgIyAzMDQ2MDk5ICAtLSAgcmVzdWx0cyBmcm9tIGEgcm91dGUgY2hhbmdlLCBhbmQgYSAzaHIgd2FpdCBiZWZvcmUgdGhlIG5ldyByb3V0ZSBzdGFydHMKICAgICAgICAgICApCiAgICApCgoKIyBleGFtcGxlcyBvZiBUcmF2ZWxUaW1lX1NlYyB2YWx1ZXMgdGhhdCBhcmUgdGhlIHNtYWxsZXN0IG9mIHRoZSBsYXJnZS4KVmlldyhmaWx0ZXIoTm9PdXRsaWVyc1NwZWVkLAogICAgICAgICAgICAoUm93TnVtX09HID49IDMwNDQ2ODkgJiBSb3dOdW1fT0cgPD0gMzA0NDcwOSkgfCAjIDMwNDQ2OTkgIC0tICByZXN1bHRzIGZyb20gYSByb3V0ZSBjaGFuZ2UKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gMzAyMjM1OCAmIFJvd051bV9PRyA8PSAzMDIyMzc4KSB8ICMgMzAyMjM2OCAgLS0gIHJlc3VsdHMgZnJvbSBhIHJvdXRlIGNoYW5nZQogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAyOTkzMDE2ICYgUm93TnVtX09HIDw9IDI5OTMwMzYpIHwgIyAyOTkzMDI2ICAtLSAgcmVzdWx0cyBmcm9tIGEgcHJldmlvdXMgcm91dGUgY2hhbmdlIChjaGFuZ2Ugb2NjdXJyZWQgaW4gZGVsZXRlZCByb3cpCiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDI2ODM3MDMgJiBSb3dOdW1fT0cgPD0gMjY4MzcyMykgIyAyNjgzNzEzICAtLSAgcmVzdWx0cyBmcm9tIGEgcHJldmlvdXMgcm91dGUgY2hhbmdlIChjaGFuZ2Ugb2NjdXJyZWQgaW4gZGVsZXRlZCByb3cpCiAgICAgICAgICAgKQogICAgKQoKYGBgCgoKTGV0J3MgbG9vayBhdCB0aGUgVHJhdmVsVGltZV9TZWMgdmFsdWVzIGFuZCByb3V0ZSBjaGFuZ2VzIChEaXJDaGFuZ2UyKS4KClRoZSA5OXRoIHBlcmNlbnRpbGUgb2YgVHJhdmVsVGltZV9TZWMgZm9yIGJvdGgsIGFsbCB0cmlwcywgYW5kIGp1c3QgdGhvc2UgdHJpcHMgTk9UIGludm9sdmluZyByb3V0ZSBjaGFuZ2VzIChEaXJDaGFuZ2UyID0gIlNhbWUiKSwgaXMgYXBwcm94aW1hdGVseSA1bWluICgzMDAgc2VjKS4KCk5vdGEgQmVuZTogIFRoZSBwZXJjZW50aWxlIGNhbGN1bGF0aW9uIGhlcmUgaXMgZGVmaW5lZCBzbGlnaHRseSBkaWZmZXJlbnQgdGhhbiBpbiBtb3N0IG9mIHRoZSBhYm92ZSBhbmFseXNlcyAod2hpY2ggZ2V0IHRoZSBsb3dlc3QgdmFsdWUgaW4gdGhlIGJpbiBjcmVhdGVkIGJ5IDEwMCBudGlsZXMpLgpgYGB7cn0KCnN1bW1hcnkoc2VsZWN0KE5vT3V0bGllcnNTcGVlZCwKICAgICAgICAgICAgICAgVHJhdmVsVGltZV9TZWMKICAgICAgICAgICAgICApCiAgICAgICApCgpzdW1tYXJ5KHNlbGVjdChmaWx0ZXIoTm9PdXRsaWVyc1NwZWVkLAogICAgICAgICAgICAgICAgICAgICAgRGlyQ2hhbmdlMiA9PSAiU2FtZSIKICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICAgVHJhdmVsVGltZV9TZWMKICAgICAgICAgICAgICApCiAgICAgICApCgpzdW1tYXJ5KHNlbGVjdChmaWx0ZXIoTm9PdXRsaWVyc1NwZWVkLAogICAgICAgICAgICAgICAgICAgICAgRGlyQ2hhbmdlMiA9PSAiQ2hhbmdlIgogICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICBUcmF2ZWxUaW1lX1NlYwogICAgICAgICAgICAgICkKICAgICAgICkKCgpUcmF2VGltZVNlY19RdGlsZXNfZGYgPC0gZGF0YS5mcmFtZShQY3RWYWx1ZSA9IHNlcSgwLCAxMDAsIDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBBbGwgPSBzZXEoMSwgMTAxLCAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU2FtZSA9IHNlcSgxLCAxMDEsIDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDaGFuZ2UgPSBzZXEoMSwgMTAxLCAxKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKClRyYXZUaW1lU2VjX1F0aWxlc19kZlsgLCAyXSA8LSBxdWFudGlsZShzZWxlY3QoTm9PdXRsaWVyc1NwZWVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvYnMgPSBzZXEoMCwgMSwgMC4wMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKVHJhdlRpbWVTZWNfUXRpbGVzX2RmWyAsIDNdIDwtIHF1YW50aWxlKHNlbGVjdChmaWx0ZXIoTm9PdXRsaWVyc1NwZWVkLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBEaXJDaGFuZ2UyID09ICJTYW1lIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsVGltZV9TZWMKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcm9icyA9IHNlcSgwLCAxLCAwLjAxKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCgpUcmF2VGltZVNlY19RdGlsZXNfZGZbICwgNF0gPC0gcXVhbnRpbGUoc2VsZWN0KGZpbHRlcihOb091dGxpZXJzU3BlZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIERpckNoYW5nZTIgPT0gIkNoYW5nZSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRyYXZlbFRpbWVfU2VjCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvYnMgPSBzZXEoMCwgMSwgMC4wMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKVmlldyhUcmF2VGltZVNlY19RdGlsZXNfZGYpCgpgYGAKCgpMaW1pdCB0aGUgZGF0YXNldCBub3cgYmFzZWQgb24gVHJhdmVsVGltZV9TZWMuCmBgYHtyfQoKVXBwZXJMaW1pdFRyYXZUaW1lIDwtIGZpbHRlcihOb091dGxpZXJzU3BlZWQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVHJhdmVsVGltZV9TZWMgPD0gNDkxICMgbWluIGF0IHRoZSAxMDB0aCBwZXJjZW50aWxlCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKbnJvdyhOb091dGxpZXJzU3BlZWQpIC0gbnJvdyhVcHBlckxpbWl0VHJhdlRpbWUpCgpzdHIoVXBwZXJMaW1pdFRyYXZUaW1lKQoKc3VtbWFyeShVcHBlckxpbWl0VHJhdlRpbWUpCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIER3ZWxsX1RpbWUyIChob3cgbG9uZyB0aGUgYnVzIGlzIGF0IGEgc3RvcCkuCgpEaWZmZXJlbmNlcyBiZXR3ZWVuIER3ZWxsX1RpbWUgKGJ5IFdNQVRBKSBhbmQgRHdlbGxfVGltZTIgKGJ5IG1lKSBhcHBlYXIgdG8gYmUgZHVlIHRvIHN3aXRjaGVzIGluIFJvdXRlQWx0LiBXTUFUQSBjYWxjdWxhdGVzIER3ZWxsX1RpbWUgYnkgYW4gdW5rbm93biBwcm9jZXNzLiBUaGUgV01BVEEgY2FsY3VsYXRpb24gaXMgZXF1YWwgdG8gbXkgY2FsY3VsYXRpb24sIGV4Y2VwdCBmb3IgdGhlIHJlY29yZHMgaW1tZWRhaXRlbHkgYmVmb3JlIGFuZCBhZnRlciBhIFJvdXRlQWx0IHN3aXRjaCAoRGlyQ2hhbmdlMikuCmBgYHtyfQoKVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgRHdlbGxfVGltZSAhPSBEd2VsbF9UaW1lMgogICAgICAgICAgICkKICAgICkKCgojIEV4YW1wbGVzIHdoZXJlIHRoZSBEd2VsbF9UaW1lIGFuZCBEd2VsbF9UaW1lMiBhcmUgZGlmZmVyZW50ClZpZXcoZmlsdGVyKEFsbERheXNfTmV3T3JkZXIsCiAgICAgICAgICAgICggKFJvd051bV9PRyA+PSA2NSAmIFJvd051bV9PRyA8PSA4NSkgfCAjIDc1CiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDE2MiAmIFJvd051bV9PRyA8PSAxOTIpIHwgIyAxNzIKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gNDMxOTUyICYgUm93TnVtX09HIDw9IDQzMTk3MikgfCAjIDQzMTk2MgogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSA0MzQ1OTUgJiBSb3dOdW1fT0cgPD0gNDM0NjE1KSAjIDQzNDYwNSAgLS0gIHRoaXMgcmVjb3JkIGlzIE5PVCBhIHJvdXRlIHN3aXRjaCwgYnV0IGRvZXMgaGFzIGEgU2VxdWVuY2Ugc3dpdGNoIChNZTogc2hvdWxkIHRoZXJlIHJlYWxseSBiZSBhIHJvdXRlIHN3aXRjaCBoZXJlPykKICAgICAgICAgICAgKQogICAgICAgICAgICkKICAgICkKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgRHdlbGxfVGltZTIgKGhvdyBsb25nIHRoZSBidXMgaXMgYXQgYSBzdG9wKS4KCkZpcnN0LCBjcmVhdGUgc29tZSAicmFuayIgc3RhdHMuClZpZXcoRFQyX1BjdGlsZXMpOiA5NSUgb2YgRHdlbGxfVGltZTJzIGFyZSA8PSAyMyBzZWNvbmRzLi4uYnV0IHNvbWUgd2VpcmQgKGUuZy4sIG5lYXJseSAyIGhvdXIgRHdlbGxfVGltZTJzIGV4aXN0KS4KYGBge3J9CgpEd2VsbFRpbWUyX050aWxlIDwtIGFzLmRhdGEuZnJhbWUoQWxsRGF5c19OZXdPcmRlciREd2VsbF9UaW1lMikgJT4lIAogIG11dGF0ZShQY3RpbGUgPSBudGlsZShBbGxEYXlzX05ld09yZGVyJER3ZWxsX1RpbWUyLCAxMDApLAogICAgICAgICBNaW5SID0gbWluX3JhbmsoQWxsRGF5c19OZXdPcmRlciREd2VsbF9UaW1lMiksCiAgICAgICAgIFBjdFIgPSBwZXJjZW50X3JhbmsoQWxsRGF5c19OZXdPcmRlciREd2VsbF9UaW1lMiksCiAgICAgICAgIFBjdFJfUm91bmQgPSByb3VuZChQY3RSLCAyKQogICAgICAgICkgCgpjb2xuYW1lcyhEd2VsbFRpbWUyX050aWxlKVsxXSA8LSAiRHdlbGxfVGltZTIiCnN0cihEd2VsbFRpbWUyX050aWxlKQoKRHdlbGxUaW1lMl9OdGlsZV9Sb3dzIDwtIG5yb3coRHdlbGxUaW1lMl9OdGlsZSkKClZpZXcodGFpbChEd2VsbFRpbWUyX050aWxlLCA1MDApKQoKCkR3ZWxsVGltZTJfUGN0aWxlcyA8LSBncm91cF9ieShEd2VsbFRpbWUyX050aWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUGN0Ul9Sb3VuZAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzdW1tYXJpc2UoCiAgICBNaW5Ed2VsbEF0UGN0aWxlID0gbWluKER3ZWxsX1RpbWUyKSwKICAgIENudHNBdFBjdGlsZSA9IG4oKSwKICAgIFBjdHNBdFBjdGlsZSA9IENudHNBdFBjdGlsZSAvIER3ZWxsVGltZTJfTnRpbGVfUm93cwogICkgJT4lIAogIG11dGF0ZShDdW1TdW1QQXRQID0gY3Vtc3VtKFBjdHNBdFBjdGlsZSkKICAgICAgICApCgpWaWV3KER3ZWxsVGltZTJfUGN0aWxlcykKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgRHdlbGxfVGltZTIgKGhvdyBsb25nIHRoZSBidXMgaXMgYXQgYSBzdG9wKS4KCkhpc3RvZ3JhbSBvZiBEd2VsbF9UaW1lMi4KYGBge3J9CgpEd2VsbFRpbWUyX0hpc3REZW4gPC0gZ2dwbG90KEFsbERheXNfTmV3T3JkZXIsIGFlcyh4ID0gRHdlbGxfVGltZTIsIHkgPSAuLmRlbnNpdHkuLikpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEsIGZpbGwgPSAibGlnaHRibHVlIiwgY29sb3VyID0gImdyZXk2MCIsIHNpemUgPSAwLjIpICsKICBnZW9tX2xpbmUoc3RhdCA9ICJkZW5zaXR5IiwgY29sb3VyID0gInJlZCIpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoMSwgMjUpLCB5bGltID0gYygwLCAwLjA1KQogICAgICAgICAgICAgICAgICkgKwogIHhsYWIoIlRpbWUgYSBCdXMgU3RheXMgYXQgYSBTdG9wIChzZWMpIikgKyAKICB5bGFiKCJEZW5zaXR5IikgKyAKICAjICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArIAogIGdndGl0bGUoZXhwcmVzc2lvbihhdG9wKCJWYXJpYXRpb24gaW4gSG93IExvbmcgYSBCdXMgU3RheXMgYXQgYSBTdG9wIgogICAgICAgICAgICAgICAgICAgICAgICAgICMgLGF0b3AoaXRhbGljKCJ4eHh4eCIpLCIiKQogICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgKQoKRHdlbGxUaW1lMl9IaXN0RGVuCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIER3ZWxsX1RpbWUyIChob3cgbG9uZyB0aGUgYnVzIGlzIGF0IGEgc3RvcCkuCgpMb29raW5nIGF0IHNvbWUgd2VpcmRseSBsb25nIER3ZWxsX1RpbWUyIHZhbHVlcy4KYGBge3J9CgpWaWV3KGFycmFuZ2UoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgIGRlc2MoRHdlbGxfVGltZTIpCiAgICAgICAgICAgICkKICAgICkKCgojIGV4YW1wbGVzIG9mIGV4dHJlbWVseSBsYXJnZSBEd2VsbF9UaW1lMnMKVmlldyhmaWx0ZXIoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgKFJvd051bV9PRyA+PSAyOTI2NjkgJiBSb3dOdW1fT0cgPD0gMjkyNjg5KSB8ICMgMjkyNjc5CiAgICAgICAgICAgICAgICAoUm93TnVtX09HID49IDUzMTA1NyAmIFJvd051bV9PRyA8PSA1MzEwNzcpIHwgIyA1MzEwNjcKICAgICAgICAgICAgICAgIChSb3dOdW1fT0cgPj0gMTM4ODYyNyAmIFJvd051bV9PRyA8PSAxMzg4NjQ3KSB8ICMgMTM4ODYzNwogICAgICAgICAgICAgICAgKFJvd051bV9PRyA+PSAxNjQ1NzExICYgUm93TnVtX09HIDw9IDE2NDU3MzEpICMgMTY0NTcyMQogICAgICAgICAgICkKICAgICkKCgpWaWV3KGZpbHRlcihBbGxEYXlzX05ld09yZGVyLAogICAgICAgICAgICBEd2VsbF9UaW1lMiA9PSAwCiAgICAgICAgICAgKQogICAgKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBEZWx0YV9UaW1lIChob3cgZWFybHkgb3IgbGF0ZSB0aGUgYnVzIGlzKS4KClZpZXcoRFQyX1BjdGlsZXMpOiA5NCUgb2YgRGVsdGFfVGltZSB2YWx1ZXMgYXJlIGJldHdlZW4gLTIzNiBzZWNvbmRzIGFuZCAxLDI1OSBzZWNvbmRzLiBSb3VnaGx5IDY2JSBvZiByZWNvcmRzIGFyZSB3aXRoaW4gNSBtaW4gbGF0ZSBhbmQgNSBtaW4gZWFybHkuLi5idXQgc29tZSB3ZWlyZCAoZS5nLiwgYWxtb3N0IDUwIG1pbnV0ZSBsYXRlIG9yIDQwIG1pbnV0ZSBlYXJseSkgRGVsdGFfVGltZXMgZXhpc3QuCgpOb3RlIHRoYXQgRGVsdGFfVGltZSBpcyB0aGUgZGlmZmVyZW5jZSBmcm9tIHRoZSBzY2hlZHVsZWQgYnVzIGFycml2YWwuIFNvIGlmIHR3byBidXNlcyBhcmUgc2NoZWR1bGVkIHRvIGFycml2ZSBhdCBhIGRlc3RpbmF0aW9uIGF0IDEwOjAwcG0gYW5kIDEwOjIwcG0sIGFuZCBpZiB0aGUgMTA6MjBwbSBidXMgaGFzIGEgRGVsdGFfVGltZSBvZiA1IG1pbnV0ZXMsIHRoZXJlIGFyZSAyNSBtaW51dGVzIGJldHdlZW4gYnVzIGFycml2YWxzIGF0IHRoZSBzdG9wLgoKQWxzbyBub3RlIHRoYXQgYmFzZWQgb24gYSBjb21tZW50IGF0IGh0dHBzOi8vcGxhbml0bWV0cm8uY29tLzIwMTYvMTEvMTYvZGF0YS1kb3dubG9hZC1tZXRyb2J1cy12ZWhpY2xlLWxvY2F0aW9uLWRhdGEvLCB0aGUgRGVsdGFfVGltZSB2YWx1ZXMgZG9uJ3QgYXBwZWFyIHRvIGNvaW5jaWRlIHdpdGggcHVibGlzaGVkIGJ1cyBzY2hlZHVsZXMgKGUuZy4sIHRoZSBYMiBkZXBhcnRpbmcgZXZlcnkgOCBtaW51dGVzIGR1cmluZyBwZWFrIGhvdXJzKS4KYGBge3J9CgpEZWx0VGltZV9OdGlsZSA8LSBhcy5kYXRhLmZyYW1lKEFsbERheXNfTmV3T3JkZXIkRGVsdGFfVGltZSkgJT4lIAogIG11dGF0ZShQY3RpbGUgPSBudGlsZShBbGxEYXlzX05ld09yZGVyJERlbHRhX1RpbWUsIDEwMCksCiAgICAgICAgIE1pblIgPSBtaW5fcmFuayhBbGxEYXlzX05ld09yZGVyJERlbHRhX1RpbWUpLAogICAgICAgICBQY3RSID0gcGVyY2VudF9yYW5rKEFsbERheXNfTmV3T3JkZXIkRGVsdGFfVGltZSksCiAgICAgICAgIFBjdFJfUm91bmQgPSByb3VuZChQY3RSLCAyKQogICAgICAgICkgCgpjb2xuYW1lcyhEZWx0VGltZV9OdGlsZSlbMV0gPC0gIkRlbHRhX1RpbWUiCnN0cihEZWx0VGltZV9OdGlsZSkKCkRlbHRUaW1lX050aWxlX1Jvd3MgPC0gbnJvdyhEZWx0VGltZV9OdGlsZSkKClZpZXcodGFpbChEZWx0VGltZV9OdGlsZSwgNTAwKSkKCgpEZWx0VGltZV9QY3RpbGVzIDwtIGdyb3VwX2J5KERlbHRUaW1lX050aWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBjdFJfUm91bmQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHN1bW1hcmlzZSgKICAgIE1pbkRlbHRUaW1lQXRQY3RpbGUgPSBtaW4oRGVsdGFfVGltZSksCiAgICBDbnRzQXRQY3RpbGUgPSBuKCksCiAgICBQY3RzQXRQY3RpbGUgPSBDbnRzQXRQY3RpbGUgLyBEZWx0VGltZV9OdGlsZV9Sb3dzCiAgKSAlPiUgCiAgbXV0YXRlKEN1bVN1bVBBdFAgPSBjdW1zdW0oUGN0c0F0UGN0aWxlKQogICAgICAgICkKClZpZXcoRGVsdFRpbWVfUGN0aWxlcykKRGVsdFRpbWVfUGN0aWxlcwoKIyB+NjYlIG9mIHJvd3MgYXJlIGJldHdlZW4gNSBtaW4gbGF0ZSBhbmQgNSBtaW4gZWFybHkKbnJvdyhmaWx0ZXIoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgRGVsdGFfVGltZSA+PSAtMzAwICYKICAgICAgICAgICAgICBEZWx0YV9UaW1lIDw9IDMwMAogICAgICAgICAgICkKICAgICkgLyBucm93KEFsbERheXNfTmV3T3JkZXIpCgoKIyBleGFtcGxlcyBvZiB3ZWlyZCBsYXJnZSBEZWx0YV9UaW1lcwpWaWV3KGZpbHRlcihBbGxEYXlzX05ld09yZGVyLAogICAgICAgICAgICBEZWx0YV9UaW1lIDwgLTQyMDIgfAogICAgICAgICAgICAgIERlbHRhX1RpbWUgPiAxNzA1CiAgICAgICAgICAgKSAlPiUgCiAgICAgICBhcnJhbmdlKGRlc2MoRGVsdGFfVGltZSkKICAgICAgICAgICAgICApCiAgICApCgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIERlbHRhX1RpbWUgKGhvdyBlYXJseSBvciBsYXRlIHRoZSBidXMgaXMpLgoKRGVsdGFfVGltZSBoaXN0b2dyYW0uCmBgYHtyfQoKRGVsdFRpbWVfSGlzdERlbiA8LSBnZ3Bsb3QoQWxsRGF5c19OZXdPcmRlciwgYWVzKHggPSAoRGVsdGFfVGltZSAvIDYwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSAuLmRlbnNpdHkuLgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICAgKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAoNS82MCksIGZpbGwgPSAibGlnaHRibHVlIiwgY29sb3VyID0gImdyZXk2MCIsIHNpemUgPSAwLjIpICsKICBnZW9tX2xpbmUoc3RhdCA9ICJkZW5zaXR5IiwgY29sb3VyID0gInJlZCIpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoLTUsIDUpKSArCiAgeGxhYigiQnVzIExhdGVuZXNzIChtaW4pIikgKyAKICB5bGFiKCJEZW5zaXR5IikgKyAKICAjICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArIAogIGdndGl0bGUoZXhwcmVzc2lvbihhdG9wKCJWYXJpYXRpb24gaW4gSG93IEVhcmx5L0xhdGUgYSBCdXMgSXMiLAogICAgICAgICAgICAgICAgICAgICAgICAgIGF0b3AoaXRhbGljKCIocG9zaXRpdmUgdmFsdWVzIGFyZSBsYXRlIGFycml2YWxzKSIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiIKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgKQoKRGVsdFRpbWVfSGlzdERlbgoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBEZWx0YV9UaW1lIChob3cgZWFybHkgb3IgbGF0ZSB0aGUgYnVzIGlzKS4KCkRlbHRhX1RpbWUgYm94cGxvdC4KYGBge3J9CgojIENvdW50X1ZhbHVlcyBpcyBuZWVkZWQgdG8gZGlzcGxheSB0aGUgbWVkaWFucyBvbiB0aGUgYm94IHBsb3RzCkNvdW50X1ZhbHVlcyA8LSBkZHBseShBbGxEYXlzX05ld09yZGVyLAogICAgICAgICAgICAgICAgICAgICAgLihFdmVudF9UaW1lX0hyR3JvdXApLAogICAgICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlLAogICAgICAgICAgICAgICAgICAgICAgVmFsdWVfQ291bnRzID0gbWVkaWFuKERlbHRhX1RpbWUgLyA2MCwgbmEucm0gPSBUUlVFKQogICAgICAgICAgICAgICAgICAgICApCgpEZWx0VGltZV9Cb3hQbG90IDwtIGdncGxvdChBbGxEYXlzX05ld09yZGVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoZmFjdG9yKEV2ZW50X1RpbWVfSHJHcm91cCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBEZWx0YV9UaW1lIC8gNjAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gZmFjdG9yKEV2ZW50X1RpbWVfSHJHcm91cCkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICkgKyAKICBnZW9tX2JveHBsb3Qob3V0bGllci5jb2xvdXI9InJlZCIsIG5vdGNoPVRSVUUpICsgCiAgIyBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoLTMwMCwgMTIwMCkpICsKICBjb29yZF9jYXJ0ZXNpYW4oeWxpbSA9IGMoLTUsIDIwKSkgKwogIGdlb21fdGV4dChkYXRhID0gQ291bnRfVmFsdWVzLAogICAgICAgICAgICBhZXMoeSA9IFZhbHVlX0NvdW50cywKICAgICAgICAgICAgICAgIGxhYmVsID0gZm9ybWF0KHJvdW5kKFZhbHVlX0NvdW50cywgZGlnaXRzID0gMSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuc21hbGwgPSAxCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgc2l6ZSA9IDMsCiAgICAgICAgICAgIHZqdXN0ID0gLTAuNQogICAgICAgICAgICkgKwogIHhsYWIoIkhvdXIgR3JvdXAiKSArIAogIHlsYWIoIkJ1cyBMYXRlbmVzcyAobWludXRlcykiKSArIAogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIsIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlPTQ1KSkgKyAKICAjdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJyaWdodCIsIGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpKSArIAogIGdndGl0bGUoZXhwcmVzc2lvbihhdG9wKCJIb3cgRWFybHkvTGF0ZSBpcyB0aGUgQnVzIChieSBIb3VyIEdyb3VwKSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgYXRvcChpdGFsaWMoIihwb3NpdGl2ZSB2YWx1ZXMgYXJlIGxhdGUgYXJyaXZhbHMpIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICApCgpEZWx0VGltZV9Cb3hQbG90CgpgYGAKCgpJbnZlc3RpZ2F0aW9uIG9mIERlbHRhX1RpbWUgKGhvdyBlYXJseSBvciBsYXRlIHRoZSBidXMgaXMpLgoKRXhwbG9yaW5nICJleHRyZW1lIiBEZWx0YV9UaW1lcy4gIEZpcnN0IGxldCdzIGdldCBzb21lICJyYW5rIiBzdGF0cy4KYGBge3J9CgpWaWV3KERlbHRUaW1lX1BjdGlsZXMpCkRlbHRUaW1lX1BjdGlsZXMKCgpEZWx0VGltZUFic19OdGlsZSA8LSBhcy5kYXRhLmZyYW1lKGFicyhBbGxEYXlzX05ld09yZGVyJERlbHRhX1RpbWUpKSAlPiUgCiAgbXV0YXRlKFBjdGlsZSA9IG50aWxlKGFicyhBbGxEYXlzX05ld09yZGVyJERlbHRhX1RpbWUpLCAxMDApLAogICAgICAgICBNaW5SID0gbWluX3JhbmsoYWJzKEFsbERheXNfTmV3T3JkZXIkRGVsdGFfVGltZSkpLAogICAgICAgICBQY3RSID0gcGVyY2VudF9yYW5rKGFicyhBbGxEYXlzX05ld09yZGVyJERlbHRhX1RpbWUpKSwKICAgICAgICAgUGN0Ul9Sb3VuZCA9IHJvdW5kKFBjdFIsIDIpCiAgICAgICAgKSAKCmNvbG5hbWVzKERlbHRUaW1lQWJzX050aWxlKVsxXSA8LSAiRGVsdGFfVGltZV9BYnMiCnN0cihEZWx0VGltZUFic19OdGlsZSkKCkRlbHRUaW1lQWJzX050aWxlX1Jvd3MgPC0gbnJvdyhEZWx0VGltZUFic19OdGlsZSkKClZpZXcodGFpbChEZWx0VGltZUFic19OdGlsZSwgNTAwKSkKCgpEZWx0VGltZUFic19QY3RpbGVzIDwtIGdyb3VwX2J5KERlbHRUaW1lQWJzX050aWxlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBjdFJfUm91bmQKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIHN1bW1hcmlzZSgKICAgIE1pbkRlbHRUaW1lQXRQY3RpbGUgPSBtaW4oRGVsdGFfVGltZV9BYnMpLAogICAgQ250c0F0UGN0aWxlID0gbigpLAogICAgUGN0c0F0UGN0aWxlID0gQ250c0F0UGN0aWxlIC8gRGVsdFRpbWVfTnRpbGVfUm93cwogICkgJT4lIAogIG11dGF0ZShDdW1TdW1QQXRQID0gY3Vtc3VtKFBjdHNBdFBjdGlsZSkKICAgICAgICApCgpWaWV3KERlbHRUaW1lQWJzX1BjdGlsZXMpCkRlbHRUaW1lQWJzX1BjdGlsZXMKCmBgYAoKCkludmVzdGlnYXRpb24gb2YgRGVsdGFfVGltZSAoaG93IGVhcmx5IG9yIGxhdGUgdGhlIGJ1cyBpcykuCgpFeHBsb3JpbmcgImV4dHJlbWUiIERlbHRhX1RpbWVzLiAgVGhlbiBsZXQncyBjYWxjdWxhdGUgdGhlIHBlcmNlbnRhZ2Ugb2YgYnVzZXMgdGhhdCBhcmUgMTAgbWludXRlcyAob3IgbW9yZSkgbGF0ZS9lYXJseS4KYGBge3J9CgpIckdyb3VwX0RlbHRhVGltZV9BbGwgPC0gZ3JvdXBfYnkoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEV2ZW50X1RpbWVfSHJHcm91cAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICU+JSAKICBzdW1tYXJpc2UoRXZlbnRBbGxfQ250ID0gbigpCiAgICAgICAgICAgKQoKc3RyKEhyR3JvdXBfRGVsdGFUaW1lX0FsbCkKVmlldyhIckdyb3VwX0RlbHRhVGltZV9BbGwpCgoKSHJHcm91cF9EZWx0YVRpbWVfQWJvdmUxME1pbiA8LSBmaWx0ZXIoQWxsRGF5c19OZXdPcmRlciwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWJzKERlbHRhX1RpbWUpID49IDYwMAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIGdyb3VwX2J5KEV2ZW50X1RpbWVfSHJHcm91cCkgJT4lIAogIHN1bW1hcmlzZShFdmVudEFib3ZlMTBfQ250ID0gbigpCiAgICAgICAgICAgKQoKc3RyKEhyR3JvdXBfRGVsdGFUaW1lX0Fib3ZlMTBNaW4pClZpZXcoSHJHcm91cF9EZWx0YVRpbWVfQWJvdmUxME1pbikKCgpIckdyb3VwX0RlbHRhVGltZUNvbXBhcmUgPC0gaW5uZXJfam9pbihIckdyb3VwX0RlbHRhVGltZV9BYm92ZTEwTWluLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBIckdyb3VwX0RlbHRhVGltZV9BbGwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gYygiRXZlbnRfVGltZV9Ickdyb3VwIiA9ICJFdmVudF9UaW1lX0hyR3JvdXAiKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgJT4lIAogIG11dGF0ZShQY3RFdmVudHNBYm92ZTEwID0gRXZlbnRBYm92ZTEwX0NudCAvIEV2ZW50QWxsX0NudCkKClZpZXcoSHJHcm91cF9EZWx0YVRpbWVDb21wYXJlKQoKYGBgCgoKSW52ZXN0aWdhdGlvbiBvZiBEZWx0YV9UaW1lIChob3cgZWFybHkgb3IgbGF0ZSB0aGUgYnVzIGlzKS4KClF1aWNrbHkgcGxvdCB0aGVzZSAiZXh0cmVtZSIgRGVsdGFfVGltZXMuIApgYGB7cn0KCkRlbHRUaW1lX0Fib3ZlMTBfQ29scyA8LSBnZ3Bsb3QoSHJHcm91cF9EZWx0YVRpbWVDb21wYXJlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyhmYWN0b3IoRXZlbnRfVGltZV9Ickdyb3VwKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgUGN0RXZlbnRzQWJvdmUxMAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgKwogIGdlb21fY29sKGZpbGwgPSAibGlnaHRibHVlIiwgY29sb3VyID0gImdyZXk2MCIsIHNpemUgPSAwLjIpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gZm9ybWF0KHJvdW5kKFBjdEV2ZW50c0Fib3ZlMTAsIGRpZ2l0cyA9IDIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbnNtYWxsID0gMgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICApCiAgICAgICAgICAgICAgICksCiAgICAgICAgICAgIHNpemUgPSAzLAogICAgICAgICAgICBudWRnZV95ID0gKEhyR3JvdXBfRGVsdGFUaW1lQ29tcGFyZSRQY3RFdmVudHNBYm92ZTEwICogLTAuMSkKICAgICAgICAgICApICsKICAjIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygtNSwgNSkpICsKICB4bGFiKCJIb3VyIEdyb3VwIikgKyAKICB5bGFiKCJQZXJjZW50IG9mIEFsbCBCdXMgQXJyaXZhbHMiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIiwgYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGU9NDUpKSArCiAgZ2d0aXRsZShleHByZXNzaW9uKGF0b3AoIldoZW4gaXMgYSBCdXMgMTArIE1pbnV0ZXMgTGF0ZS9FYXJseSIKICAgICAgICAgICAgICAgICAgICAgICAgICAjICxhdG9wKGl0YWxpYygicG9zaXRpdmUgdmFsdWVzIGFyZSBsYXRlIGFycml2YWxzIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgIyAgICAgICIiCiAgICAgICAgICAgICAgICAgICAgICAgICAgIyAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICkKICAgICAgICAgKQoKRGVsdFRpbWVfQWJvdmUxMF9Db2xzCgpgYGAKCgpRdWljayBpbnZlc3RpZ2F0aW9uIG9uIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBEd2VsbF9UaW1lMiAodGhlIHRpbWUgYSBidXMgaXMgYXQgYSBzdG9wKSBhbmQgRGVsdGFfVGltZSAoaG93IGVhcmx5L2xhdGUgdGhlIGJ1cyBpcykuCgpDb3JyZWxhdGlvbi4KYGBge3J9CgpEd2VsbFREZWx0YVRfQ29yciA8LSBhcy5tYXRyaXgoY29yKHggPSBBbGxEYXlzX05ld09yZGVyJER3ZWxsX1RpbWUyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBBbGxEYXlzX05ld09yZGVyJERlbHRhX1RpbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdXNlID0gInBhaXJ3aXNlIgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKRHdlbGxURGVsdGFUX0NvcnIKCmBgYAoKClF1aWNrIGludmVzdGlnYXRpb24gb24gdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIER3ZWxsX1RpbWUyICh0aGUgdGltZSBhIGJ1cyBpcyBhdCBhIHN0b3ApIGFuZCBEZWx0YV9UaW1lIChob3cgZWFybHkvbGF0ZSB0aGUgYnVzIGlzKS4KCk5leHQsIGxldCdzIGdldCBhIHNhbXBsZSBvZiBkYXRhIGZvciBwbG90dGluZy4gTGV0J3MgZG8gdGhpcyBmb3IgdGhlIGZ1bGwgZGF0YXNldCAoQWxsRGF5c19OZXdPcmRlcikuCmBgYHtyfQoKQWxsRGF5c19OZXdPcmRlcl8xMFBjdFNhbXAgPC0gc2FtcGxlX2ZyYWMoQWxsRGF5c19OZXdPcmRlciwgMC4xKSAlPiUgCiAgc2VsZWN0KERlbHRhX1RpbWUsCiAgICAgICAgIER3ZWxsX1RpbWUyCiAgICAgICAgKSAlPiUgCiAgbXV0YXRlKERhdGFTZXQgPSAiQWxsRGF0YSIpCgpzdHIoQWxsRGF5c19OZXdPcmRlcl8xMFBjdFNhbXApCgpgYGAKCgpRdWljayBpbnZlc3RpZ2F0aW9uIG9uIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBEd2VsbF9UaW1lMiAodGhlIHRpbWUgYSBidXMgaXMgYXQgYSBzdG9wKSBhbmQgRGVsdGFfVGltZSAoaG93IGVhcmx5L2xhdGUgdGhlIGJ1cyBpcykuCgpMZXQncyBhbHNvIGdldCBhIHNhbXBsZSBvZiBkYXRhIGZvciBwbG90dGluZywgYnV0IHdpdGggYSBkYXRzZXQgdGhhdCByZW1vdmVzIG91dGxpZXJzLgpgYGB7cn0KClZpZXcoRGVsdFRpbWVfUGN0aWxlcykKVmlldyhEd2VsbFRpbWUyX1BjdGlsZXMpCgpBbGxEYXlzX05ld09yZGVyX05vRXh0cmVtZXNfMTBQY3RTYW1wIDwtIGZpbHRlcihBbGxEYXlzX05ld09yZGVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiZXR3ZWVuKERlbHRhX1RpbWUsIC00MDIsIDE3MDUpICYgIyByZW1vdmVzIGFib3V0IDIlIG9mIERlbHRhX1RpbWUgdmFsdWVzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmV0d2VlbihEd2VsbF9UaW1lMiwgMSwgNjMpICAjIHJlbW92ZXMgYWJvdXQgMiUgb2YgRHdlbGxfVGltZTIgdmFsdWVzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKSAlPiUgCiAgc2FtcGxlX2ZyYWMoMC4xKSAlPiUgCiAgc2VsZWN0KERlbHRhX1RpbWUsCiAgICAgICAgIER3ZWxsX1RpbWUyCiAgICAgICAgKSAlPiUgCiAgbXV0YXRlKERhdGFTZXQgPSAiT3V0bGllcnNSZW1vdmVkIikKCnN0cihBbGxEYXlzX05ld09yZGVyX05vRXh0cmVtZXNfMTBQY3RTYW1wKQoKYGBgCgoKUXVpY2sgaW52ZXN0aWdhdGlvbiBvbiB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gRHdlbGxfVGltZTIgKHRoZSB0aW1lIGEgYnVzIGlzIGF0IGEgc3RvcCkgYW5kIERlbHRhX1RpbWUgKGhvdyBlYXJseS9sYXRlIHRoZSBidXMgaXMpLgoKUGxvdHRpbmcgdGhlIGRhdGEgZnJvbSB0aGUgZGF0YXNldCB0aGF0IGRvZXMgbm90IHJlbW92ZSBvdXRsaWVycy4KYGBge3J9CgpEd2VsbFREZWx0YVRfU2NhdHRlciA8LSBnZ3Bsb3QoQWxsRGF5c19OZXdPcmRlcl8xMFBjdFNhbXAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhZXMoRHdlbGxfVGltZTIsIERlbHRhX1RpbWUpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkgKwogIGdlb21fcG9pbnQoc2hhcGUgPSAxLCBhbHBoYSA9IDAuNSkgKwogIHNjYWxlX3NoYXBlKHNvbGlkID0gRkFMU0UpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBjb2xvdXIgPSAicmVkIikgKwogICMgeGxhYigiVGltZSBhdCBTdG9wIChzZWMpIikgKyAKICAjIHlsYWIoIkxhdGVuZXNzIChzZWMpIikgKwogIGFubm90YXRlKGxhYmVsID0gbG1fZXFuKGRmID0gQWxsRGF5c19OZXdPcmRlcl8xMFBjdFNhbXAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IEFsbERheXNfTmV3T3JkZXJfMTBQY3RTYW1wJERlbHRhX1RpbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IEFsbERheXNfTmV3T3JkZXJfMTBQY3RTYW1wJER3ZWxsX1RpbWUyCiAgICAgICAgICAgICAgICAgICAgICAgICApLAogICAgICAgICAgIHggPSAyMjAwLAogICAgICAgICAgIHkgPSA2MDAsCiAgICAgICAgICAgZ2VvbSA9ICJ0ZXh0IiwKICAgICAgICAgICBzaXplID0gMywKICAgICAgICAgICBjb2xvdXIgPSAicmVkIiwKICAgICAgICAgICBwYXJzZSA9IFRSVUUKICAgICAgICAgICkgKwogIGxhYnModGl0bGUgPSAiTGF0ZW5lc3MgdnMgVGltZSBhdCBTdG9wIiwKICAgICAgIHN1YnRpdGxlID0gIihubyBvdXRsaWVycyByZW1vdmVkKSIsCiAgICAgICB4ID0gIlRpbWUgYXQgU3RvcCAoc2VjKSIsCiAgICAgICB5ID0gIkxhdGVuZXNzIChzZWMpIgogICAgICApCiAgIyBnZ3RpdGxlKGV4cHJlc3Npb24oYXRvcCgiTGF0ZW5lc3MgdnMgVGltZSBhdCBTdG9wIgogICMgICAgICAgICAgICAgICAgICAgICAgICAgLGF0b3AoaXRhbGljKCIobm8gb3V0bGllcnMgcmVtb3ZlZCkiKSwKICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIiCiAgIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICkKICAjICAgICAgICAgICAgICAgICAgICAgICAgKQogICMgICAgICAgICAgICAgICAgICAgKQogICMgICAgICAgICkKIyArCiMgICBnZW9tX2ppdHRlcigpCgpEd2VsbFREZWx0YVRfU2NhdHRlcgoKYGBgCgoKUXVpY2sgaW52ZXN0aWdhdGlvbiBvbiB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gRHdlbGxfVGltZTIgKHRoZSB0aW1lIGEgYnVzIGlzIGF0IGEgc3RvcCkgYW5kIERlbHRhX1RpbWUgKGhvdyBlYXJseS9sYXRlIHRoZSBidXMgaXMpLgoKUGxvdHRpbmcgdGhlIGRhdGEgZnJvbSB0aGUgZGF0YXNldCB0aGF0IGRvZXMgcmVtb3ZlIG91dGxpZXJzLgpgYGB7cn0KCkR3ZWxsVERlbHRhVF9TY2F0dGVyX05vRXh0cmVtZXMgPC0gZ2dwbG90KEFsbERheXNfTmV3T3JkZXJfTm9FeHRyZW1lc18xMFBjdFNhbXAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyhEd2VsbF9UaW1lMiwgRGVsdGFfVGltZSkKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICsKICBnZW9tX3BvaW50KHNoYXBlID0gMSwgYWxwaGEgPSAwLjUpICsKICBzY2FsZV9zaGFwZShzb2xpZCA9IEZBTFNFKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgY29sb3VyID0gImJsdWUiKSArCiAgIyB4bGFiKCJUaW1lIGF0IFN0b3AgKHNlYykiKSArIAogICMgeWxhYigiTGF0ZW5lc3MgKHNlYykiKSArCiAgYW5ub3RhdGUobGFiZWwgPSBsbV9lcW4oZGYgPSBBbGxEYXlzX05ld09yZGVyX05vRXh0cmVtZXNfMTBQY3RTYW1wLAogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBBbGxEYXlzX05ld09yZGVyX05vRXh0cmVtZXNfMTBQY3RTYW1wJERlbHRhX1RpbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IEFsbERheXNfTmV3T3JkZXJfTm9FeHRyZW1lc18xMFBjdFNhbXAkRHdlbGxfVGltZTIKICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgeCA9IDUwLAogICAgICAgICAgIHkgPSAtNDc1LAogICAgICAgICAgIGdlb20gPSAidGV4dCIsCiAgICAgICAgICAgc2l6ZSA9IDMsCiAgICAgICAgICAgY29sb3VyID0gImJsdWUiLAogICAgICAgICAgIHBhcnNlID0gVFJVRQogICAgICAgICAgKSArCiAgbGFicyh0aXRsZSA9ICJMYXRlbmVzcyB2cyBUaW1lIGF0IFN0b3AiLAogICAgICAgc3VidGl0bGUgPSAiKDIlIG9mIG91dGxpZXJzIHJlbW92ZWQpIiwKICAgICAgIHggPSAiVGltZSBhdCBTdG9wIChzZWMpIiwKICAgICAgIHkgPSAiTGF0ZW5lc3MgKHNlYykiCiAgICAgICkKICAjIGdndGl0bGUoZXhwcmVzc2lvbihhdG9wKCJMYXRlbmVzcyB2cyBUaW1lIGF0IFN0b3AiCiAgIyAgICAgICAgICAgICAgICAgICAgICAgICAsYXRvcChpdGFsaWMoIigyJSBvZiBvdXRsaWVycyByZW1vdmVkKSIpLAogICMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiIKICAjICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICMgICAgICAgICAgICAgICAgICAgICAgICApCiAgIyAgICAgICAgICAgICAgICAgICApCiAgIyAgICAgICAgKQojICsKIyAgIGdlb21faml0dGVyKCkKCkR3ZWxsVERlbHRhVF9TY2F0dGVyX05vRXh0cmVtZXMKCmBgYAoKClF1aWNrIGludmVzdGlnYXRpb24gb24gdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIER3ZWxsX1RpbWUyICh0aGUgdGltZSBhIGJ1cyBpcyBhdCBhIHN0b3ApIGFuZCBEZWx0YV9UaW1lIChob3cgZWFybHkvbGF0ZSB0aGUgYnVzIGlzKS4KClBsb3R0aW5nIHRoZSBkYXRhIGZyb20gYm90aCBkYXRhc2V0cyB0b2dldGhlci4KYGBge3J9CgpDb21iaW5lZERhdGEgPC0gcmJpbmQoQWxsRGF5c19OZXdPcmRlcl8xMFBjdFNhbXAsCiAgICAgICAgICAgICAgICAgICAgICBBbGxEYXlzX05ld09yZGVyX05vRXh0cmVtZXNfMTBQY3RTYW1wCiAgICAgICAgICAgICAgICAgICAgICkKCkNvbWJpbmVkRGF0YSREYXRhU2V0IDwtIGZhY3RvcihDb21iaW5lZERhdGEkRGF0YVNldCkKCnN0cihDb21iaW5lZERhdGEpCgoKRHdlbGxURGVsdGFUX1NjYXR0ZXJfQ29tYmluZWQgPC0gZ2dwbG90KENvbWJpbmVkRGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gRHdlbGxfVGltZTIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IERlbHRhX1RpbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3VyID0gRGF0YVNldAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApICsKICBnZW9tX3BvaW50KHNoYXBlID0gMSwgYWxwaGEgPSAwLjUpICsKICBzY2FsZV9zaGFwZShzb2xpZCA9IEZBTFNFKSArCiAgY29vcmRfY2FydGVzaWFuKHhsaW0gPSBjKDAsIDUwMCksIHlsaW0gPSBjKC0xMDAwLCAyMDAwKQogICAgICAgICAgICAgICAgICkgKwogIGdlb21fc21vb3RoKGRhdGEgPSBmaWx0ZXIoQ29tYmluZWREYXRhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgRGF0YVNldCA9PSAiQWxsRGF0YSIKICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICBtZXRob2QgPSAibG0iLAogICAgICAgICAgICAgIGNvbG91ciA9ICJyZWQiCiAgICAgICAgICAgICApICsKICBnZW9tX3Ntb290aChkYXRhID0gZmlsdGVyKENvbWJpbmVkRGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIERhdGFTZXQgPT0gIk91dGxpZXJzUmVtb3ZlZCIKICAgICAgICAgICAgICAgICAgICAgICAgICAgKSwKICAgICAgICAgICAgICBtZXRob2QgPSAibG0iLAogICAgICAgICAgICAgIGNvbG91ciA9ICJibHVlIgogICAgICAgICAgICAgKSArCiAgIyBmYWNldF93cmFwKCB+IERhdGFTZXQsIG5jb2wgPSAyKSArCiAgYW5ub3RhdGUobGFiZWwgPSBsbV9lcW4oZGYgPSBBbGxEYXlzX05ld09yZGVyXzEwUGN0U2FtcCwKICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gQWxsRGF5c19OZXdPcmRlcl8xMFBjdFNhbXAkRGVsdGFfVGltZSwKICAgICAgICAgICAgICAgICAgICAgICAgICB4ID0gQWxsRGF5c19OZXdPcmRlcl8xMFBjdFNhbXAkRHdlbGxfVGltZTIKICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgeCA9IDMwMCwKICAgICAgICAgICB5ID0gLTYwMCwKICAgICAgICAgICBnZW9tID0gInRleHQiLAogICAgICAgICAgIHNpemUgPSAzLAogICAgICAgICAgIGNvbG91ciA9ICJyZWQiLAogICAgICAgICAgIHBhcnNlID0gVFJVRQogICAgICAgICAgKSArCiAgYW5ub3RhdGUobGFiZWwgPSBsbV9lcW4oZGYgPSBBbGxEYXlzX05ld09yZGVyX05vRXh0cmVtZXNfMTBQY3RTYW1wLAogICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBBbGxEYXlzX05ld09yZGVyX05vRXh0cmVtZXNfMTBQY3RTYW1wJERlbHRhX1RpbWUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IEFsbERheXNfTmV3T3JkZXJfTm9FeHRyZW1lc18xMFBjdFNhbXAkRHdlbGxfVGltZTIKICAgICAgICAgICAgICAgICAgICAgICAgICksCiAgICAgICAgICAgeCA9IDMwMCwKICAgICAgICAgICB5ID0gLTgwMCwKICAgICAgICAgICBnZW9tID0gInRleHQiLAogICAgICAgICAgIHNpemUgPSAzLAogICAgICAgICAgIGNvbG91ciA9ICJibHVlIiwKICAgICAgICAgICBwYXJzZSA9IFRSVUUKICAgICAgICAgICkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKSArCiAgbGFicyh0aXRsZSA9ICJMYXRlbmVzcyB2cyBUaW1lIGF0IFN0b3AiLAogICAgICAgeCA9ICJUaW1lIGF0IFN0b3AgKHNlYykiLAogICAgICAgeSA9ICJMYXRlbmVzcyAoc2VjKSIKICAgICAgKQogICMgZ2d0aXRsZShleHByZXNzaW9uKGF0b3AoIkxhdGVuZXNzIHZzIFRpbWUgYXQgU3RvcCIKICAgICAgICAgICAgICAgICAgICAgICAgICAjICxhdG9wKGl0YWxpYygiMiUgb2Ygb3V0bGllcnMgcmVtb3ZlZCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICMgICAgICAgIiIKICAgICAgICAgICAgICAgICAgICAgICAgICAjICAgICAgKQogICAgICAgICAjICAgICAgICAgICAgICAgICApCiAgICAgICAgICMgICAgICAgICAgICApCiAgICAgICAgICMgKQojICsKIyAgIGdlb21faml0dGVyKCkKCkR3ZWxsVERlbHRhVF9TY2F0dGVyX0NvbWJpbmVkCgpgYGAKCgoKCgoKCgoKCgpBZGQgYSBuZXcgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpJbnNlcnQgQ2h1bmsqIGJ1dHRvbiBvbiB0aGUgdG9vbGJhciBvciBieSBwcmVzc2luZyAqQ21kK09wdGlvbitJKi4KCldoZW4geW91IHNhdmUgdGhlIG5vdGVib29rLCBhbiBIVE1MIGZpbGUgY29udGFpbmluZyB0aGUgY29kZSBhbmQgb3V0cHV0IHdpbGwgYmUgc2F2ZWQgYWxvbmdzaWRlIGl0IChjbGljayB0aGUgKlByZXZpZXcqIGJ1dHRvbiBvciBwcmVzcyAqQ21kK1NoaWZ0K0sqIHRvIHByZXZpZXcgdGhlIEhUTUwgZmlsZSkuCg==